/*
 * REFXMLTextReaderImpl.cpp
 *
 *  Created on: Apr 1, 2013
 *      Author: Lucifer
 */

#include "REFXMLTextReaderImpl.h"
#include "XMLCharType.h"
#include "../../unicode/CharUnicodeInfo.h"

namespace BFX
{

// Initializes a new instance with specified text reader.
REFXMLTextReaderImpl::REFXMLTextReaderImpl(REFUniTextReader* pInput, REFNameTable* pNameTable)
{
	BFX_ASSERT(pInput != NULL);
	BFX_ASSERT(pNameTable != NULL);

	m_bV1Compat = true;
	m_readState	= ReadState_Initial;

	m_pNameTable = pNameTable;
	m_pNameTable->AddRef();
	m_pNamespaceManager	= new REFXMLNamespaceManager(m_pNameTable);
	m_pNodeNull = new NodeData();

	InitVariables();
	InitTextReaderInput(m_strReportedBaseUri, pInput);

	m_strReportedEncoding = m_ps->m_strEncoding;
}

REFXMLTextReaderImpl::~REFXMLTextReaderImpl()
{
	Close();
	delete[] m_pParsingStateStack;
	delete[] m_pNodes;

	m_pNameTable->Release();
}

//
// REFXMLReader override members
//
// Get the type of the current node.
XMLNodeType REFXMLTextReaderImpl::GetNodeType() const
{
	BFX_ASSERT(m_pCurNode != NULL);
	return m_pCurNode->m_type;
}

// Gets the name of the current node, including the namespace prefix.
UniString REFXMLTextReaderImpl::GetName() const
{
	BFX_ASSERT(m_pCurNode != NULL);
	return m_pCurNode->m_strNameWPrefix;
}

// Gets the name of the current node without the namespace prefix.
UniString REFXMLTextReaderImpl::GetLocalName() const
{
	BFX_ASSERT(m_pCurNode != NULL);
	return m_pCurNode->m_strLocalName;
}

// Gets the namespace URN (as defined in the W3C Namespace Specification) of the current namespace scope.
UniString REFXMLTextReaderImpl::GetNamespaceURI() const
{
	BFX_ASSERT(m_pCurNode != NULL);
	return m_pCurNode->m_strNs;
}

// Gets the namespace prefix associated with the current node.
UniString REFXMLTextReaderImpl::GetPrefix() const
{
	BFX_ASSERT(m_pCurNode != NULL);
	return m_pCurNode->m_strPrefix;
}

// Gets a value indicating whether
bool REFXMLTextReaderImpl::HasValue() const
{
	const UINT32 HasValueBitmap = 0x2659C; // 10 0110 0101 1001 1100
	// 0 None, 
	// 0 Element,
	// 1 Attribute,
	// 1 Text,
	// 1 CDATA,
	// 0 EntityReference,
	// 0 Entity,
	// 1 ProcessingInstruction,
	// 1 Comment,
	// 0 Document,
	// 1 DocumentType,
	// 0 DocumentFragment,
	// 0 Notation,
	// 1 Whitespace,
	// 1 SignificantWhitespace,
	// 0 EndElement,
	// 0 EndEntity,
	// 1 XmlDeclaration

	// This code verifies HasValueBitmap mapping of XMLNodeType to a bool specifying
	// whether the node can have a non-empty Value
	BFX_ASSERT(0 == (HasValueBitmap & (1 << (int)XMLNodeType_None)));
	BFX_ASSERT(0 == (HasValueBitmap & (1 << (int)XMLNodeType_Element)));
	BFX_ASSERT(0 != (HasValueBitmap & (1 << (int)XMLNodeType_Attribute)));
	BFX_ASSERT(0 != (HasValueBitmap & (1 << (int)XMLNodeType_Text)));
	BFX_ASSERT(0 != (HasValueBitmap & (1 << (int)XMLNodeType_CDATA)));
	BFX_ASSERT(0 == (HasValueBitmap & (1 << (int)XMLNodeType_EntityReference)));
	BFX_ASSERT(0 == (HasValueBitmap & (1 << (int)XMLNodeType_Entity)));
	BFX_ASSERT(0 != (HasValueBitmap & (1 << (int)XMLNodeType_ProcessingInstruction)));
	BFX_ASSERT(0 != (HasValueBitmap & (1 << (int)XMLNodeType_Comment)));
	BFX_ASSERT(0 == (HasValueBitmap & (1 << (int)XMLNodeType_Document)));
	BFX_ASSERT(0 != (HasValueBitmap & (1 << (int)XMLNodeType_DocumentType)));
	BFX_ASSERT(0 == (HasValueBitmap & (1 << (int)XMLNodeType_DocumentFragment)));
	BFX_ASSERT(0 == (HasValueBitmap & (1 << (int)XMLNodeType_Notation)));
	BFX_ASSERT(0 != (HasValueBitmap & (1 << (int)XMLNodeType_Whitespace)));
	BFX_ASSERT(0 != (HasValueBitmap & (1 << (int)XMLNodeType_SignificantWhitespace)));
	BFX_ASSERT(0 == (HasValueBitmap & (1 << (int)XMLNodeType_EndElement)));
	BFX_ASSERT(0 == (HasValueBitmap & (1 << (int)XMLNodeType_EndEntity)));
	BFX_ASSERT(0 != (HasValueBitmap & (1 << (int)XMLNodeType_XmlDeclaration)));
	BFX_ASSERT(m_pCurNode != NULL);

	return (0 != (HasValueBitmap & (1 << (int)m_pCurNode->m_type)));
}

// Gets the text value of the current node.
UniString REFXMLTextReaderImpl::GetValue() const
{
	bool bSuccess = true;
	if (m_parsingFunction >= ParsingFunctionPartialTextValue)
	{
		REFXMLTextReaderImpl* pThis = const_cast<REFXMLTextReaderImpl*>(this);
		if (m_parsingFunction == ParsingFunctionPartialTextValue)
		{
			bSuccess = pThis->FinishPartialValue();
			pThis->m_parsingFunction = m_nextParsingFunction;
		}
		else
		{
			bSuccess = pThis->FinishOtherValueIterator();
		}
	}

	return bSuccess ? m_pCurNode->m_strValue : UniString();
}

// Gets the base URI of the current node.
UniString REFXMLTextReaderImpl::GetBaseURI() const
{
	return m_strReportedBaseUri;
}

// Gets a value indicating whether the current node is an empty element (for example, <MyElement/>).
bool REFXMLTextReaderImpl::IsEmptyElement() const
{
	return m_pCurNode->IsEmptyElement();
}

// Returns true of the current node is a default attribute declared in DTD.
bool REFXMLTextReaderImpl::IsDefault() const
{
	return m_pCurNode->IsDefaultAttribute();
}

// Gets the xml-nametable associated with the xmlreader.
REFNameTable* REFXMLTextReaderImpl::GetNameTable() const
{
	return m_pNameTable;
}

// Moving through the stream, reads next node from the input data.
// Returns true if the next node was read successfully; false if there are no more nodes to read.
bool REFXMLTextReaderImpl::Read()
{
	while (!IsErrorOccurred())
	{
		switch (m_parsingFunction)
		{
		case ParsingFunctionElementContent:
			return ParseElementContent();
		case ParsingFunctionDocumentContent:
			return ParseDocumentContent();
		case ParsingFunctionOpenUrl:
			BFX_ASSERT (false);
			break;
		case ParsingFunctionSwitchToInteractive:
			BFX_ASSERT(!m_ps->m_bAppendMode);
			m_readState = ReadState_Interactive;
			m_parsingFunction = m_nextParsingFunction;
			continue;
		case ParsingFunctionSwitchToInteractiveXmlDecl:
			m_readState = ReadState_Interactive;
			m_parsingFunction = m_nextParsingFunction;
			if (ParseXmlDeclaration(false))
			{
				m_strReportedEncoding = m_ps->m_strEncoding;
				return true;
			}
			continue;
		case ParsingFunctionResetAttributesRootLevel:
			ResetAttributes();
			m_pCurNode = m_pNodes[m_nCurNodeIndex];
			m_parsingFunction = (m_nCurNodeIndex == 0) ? ParsingFunctionDocumentContent : ParsingFunctionElementContent;
			continue;
		case ParsingFunctionMoveToElementContent:
			ResetAttributes();
			m_nCurNodeIndex++;
			m_pCurNode = AddNode(m_nCurNodeIndex, m_nCurNodeIndex);
			m_parsingFunction = ParsingFunctionElementContent;
			continue;
		case ParsingFunctionPopElementContext:
			// pop namespace context
			m_pNamespaceManager->PopScope();
			m_parsingFunction = m_nextParsingFunction;
			BFX_ASSERT(m_parsingFunction == ParsingFunctionElementContent || m_parsingFunction == ParsingFunctionDocumentContent);
			continue;
		case ParsingFunctionPopEmptyElementContext:
			m_pCurNode = m_pNodes[m_nCurNodeIndex];
			BFX_ASSERT(m_pCurNode->m_type == XMLNodeType_Element);
			m_pCurNode->SetEmptyElement(false);
			ResetAttributes();
			// pop namespace context
            m_pNamespaceManager->PopScope();
			m_parsingFunction = m_nextParsingFunction;
			continue;
		case ParsingFunctionEntityReference:
			m_parsingFunction = m_nextParsingFunction;
			return ParseEntityReference();
		case ParsingFunctionReportEndEntity:
			BFX_ASSERT (false);
			break;
		case ParsingFunctionAfterResolveEntityInContent:
			BFX_ASSERT (false);
			break;
		case ParsingFunctionAfterResolveEmptyEntityInContent:
			BFX_ASSERT (false);
			break;
		case ParsingFunctionInReadAttributeValue:
			BFX_ASSERT (false);
			break;
		case ParsingFunctionInIncrementalRead:
			BFX_ASSERT (false);
			break;
		case ParsingFunctionFragmentAttribute:
			BFX_ASSERT (false);
			break;
		case ParsingFunctionXmlDeclarationFragment:
			BFX_ASSERT (false);
			break;
		case ParsingFunctionGoToEof:
			BFX_ASSERT (false);
			break;
		case ParsingFunctionError:
		case ParsingFunctionEof:
		case ParsingFunctionReaderClosed:
			return false;
		case ParsingFunctionNoData:
			BFX_ASSERT (false);
			break;
		case ParsingFunctionPartialTextValue:
			BFX_ASSERT (false);
			break;
		case ParsingFunctionInReadValueChunk:
			BFX_ASSERT (false);
			break;
		case ParsingFunctionInReadContentAsBinary:
			BFX_ASSERT (false);
			break;
		case ParsingFunctionInReadElementContentAsBinary:
			BFX_ASSERT (false);
			break;
		default:
			BFX_ASSERT (false);
			break;
		}
	}
	// Has error.
	return false;
}

// Closes the stream, changes the ReadState to Closed, and sets all the properties back to zero/empty string.
void REFXMLTextReaderImpl::Close()
{
	if (m_parsingFunction == ParsingFunctionReaderClosed)
	{
		return;
	}

	while (IsInEntity())
	{
		PopParsingState();
	}

	// TODO: m_ps->Close(m_bCloseInput);

	m_pCurNode = m_pNodeNull;
	m_parsingFunction = ParsingFunctionReaderClosed;
	m_strReportedEncoding.SetEmpty();
	m_strReportedBaseUri.SetEmpty();
	m_readState = ReadState_Closed;
	m_bFullAttrCleanup = false;
	ResetAttributes();
}

// Spefifies whether general entities should be automatically expanded or not
XMLEntityHandling REFXMLTextReaderImpl::GetEntityHandling() const
{
	BFX_ASSERT1(m_bV1Compat, "EntityHandling property cannot be accessed on v1Compat." );
	return m_entityHandling;
}

void REFXMLTextReaderImpl::SetEntityHandling(XMLEntityHandling entityHandling)
{
	BFX_ASSERT1(m_bV1Compat, "EntityHandling property cannot be cannot be changed on v1Compat mode.");
	BFX_ENSURE(m_readState != ReadState_Closed);	//	InvalidOperationException
	m_entityHandling = entityHandling;
}

const XMLErrorContext& REFXMLTextReaderImpl::GetLastError() const
{
	return m_errorStatus;
}
bool REFXMLTextReaderImpl::IsErrorOccurred() const
{
	return (m_errorStatus.IsErrorOccurred() || m_readState == ReadState_Error || m_parsingFunction == ParsingFunctionError);
}
void REFXMLTextReaderImpl::SetLastError(XMLError nErrorCode)
{
	SetLastError(nErrorCode, String());
}

void REFXMLTextReaderImpl::SetLastError(XMLError nErrorCode, const String& strErrorMsg)
{
	SetLastError(nErrorCode, strErrorMsg, m_ps->m_nLineNo, m_ps->GetLinePos());
}
void REFXMLTextReaderImpl::SetLastError(XMLError nErrorCode, int nPos)
{
	m_ps->m_nCharPos = nPos;
	SetLastError(nErrorCode);
}
void REFXMLTextReaderImpl::SetLastError(XMLError nErrorCode, const String& strErrorMsg, int nPos)
{
	m_ps->m_nCharPos = nPos;
	SetLastError(nErrorCode, strErrorMsg);
}

void REFXMLTextReaderImpl::SetLastError(XMLError nErrorCode, int nLineNo, int nLinePos)
{
	SetLastError(nErrorCode, String(), nLineNo, nLinePos);
}

void REFXMLTextReaderImpl::SetLastError(XMLError nErrorCode, const String& strErrorMsg, int nLineNo, int nLinePos)
{
	m_parsingFunction = ParsingFunctionError;
	m_readState = ReadState_Error;
	m_errorStatus.SetLastError(nErrorCode, strErrorMsg, nLineNo, nLinePos);
}

REFXMLSchemaInfo* REFXMLTextReaderImpl::GetDtdSchemaInfo() const
{
	return m_pDtdParserProxy == NULL ? NULL : m_pDtdParserProxy->GetDtdSchemaInfo();
}

// Disables or enables support of W3C XML 1.0 Namespaces
void REFXMLTextReaderImpl::SetSupportNamespaces(bool bSupportNamespaces)
{
	BFX_ASSERT1(m_bV1Compat, "SupportNamespaces property cannot be changed on v1Compat." );
	BFX_ENSURE(m_readState == ReadState_Initial);	// InvalidOperationException

	m_bSupportNamespaces = bSupportNamespaces;
	if (bSupportNamespaces)
	{
		m_pXmlContext->m_strDefaultNamespace = m_pNamespaceManager->LookupNamespace(UniString());
	}
	else
	{
		m_pXmlContext->m_strDefaultNamespace = UniString();
	}
}

bool REFXMLTextReaderImpl::IsSupportNamespaces() const
{
	BFX_ASSERT1(m_bV1Compat, "SupportNamespaces property cannot be accessed on v1Compat." );
	return m_bSupportNamespaces;
}

void REFXMLTextReaderImpl::InitVariables()
{
	// xml name table.
	m_pNameTable->Add(UniString());
	m_strXml		= m_pNameTable->Add(USTR("xml"));
	m_strXmlNs		= m_pNameTable->Add(USTR("xmlns"));

	// parsing state
	m_nParsingStateStackLength	= 0;
	m_pParsingStateStack	= NULL;
	m_nParsingStateStackTop	= -1;
	m_ps					= NULL;
	PushParsingState();

	// node data
	m_nNodesLength			= 16;
	m_nCurNodeIndex			= 0;
	m_nCurAttrIndex			= -1;
	m_nAttrCount			= 0;
	m_bAttrNeedNamespaceLookup	= false;
	m_bFullAttrCleanup		= false;
	m_pNodes	= new REF<NodeData>[m_nNodesLength];
	m_pNodes[m_nCurNodeIndex]	= new NodeData();
	m_pCurNode				= m_pNodes[m_nCurNodeIndex];

	// misc
	m_pStringBuilder	= new REFUniStringBuilder();
	m_pXmlContext		= new XMLContext();

	m_nNextEntityId			= 1;
	m_nReadValueOffset		= 0;

	m_nAttributeValueBaseEntityId	= 0;
	m_bEmptyEntityInAttributeResolved	= false;

	m_parsingFunction		= ParsingFunctionSwitchToInteractiveXmlDecl;
	m_nextParsingFunction	= ParsingFunctionDocumentContent;
	m_nextNextParsingFunction	= ParsingFunctionElementContent;

	m_entityHandling		= XMLEntityHandling_ExpandCharEntities;
	m_parsingMode			= ParsingModeFull;

	m_fragmentType = XMLNodeType_Document;
	m_incReadState			= IncrementalReadStateText;

	m_bRootElementParsed	= false;
	m_bSupportNamespaces	= false;
	m_bIgnorePIs			= false;
	m_bIgnoreComments		= false;
}

void REFXMLTextReaderImpl::InitTextReaderInput(const UniString& strBaseUri, REFUniTextReader* pInput)
{
	BFX_ASSERT(m_ps->m_nCharPos == 0 && m_ps->m_nCharsUsed == 0);

	m_ps->m_pTextReader	= pInput;
	m_ps->m_strBaseUri	= strBaseUri;

	// allocate character buffer if necessary.
	if (m_ps->m_pChars == NULL)
	{
		m_ps->m_nCharsLength = 4096;
		m_ps->m_pChars = new UCHAR[m_ps->m_nCharsLength + 1];
	}

	m_ps->m_strEncoding = USTR("UTF-16");

	// read first characters.
	m_ps->m_bAppendMode = true;
	ReadData();
}

void REFXMLTextReaderImpl::InitStringInput(const UniString& strBaseUri, const UniString& strOriginalEncoding, const UniString& str)
{
    BFX_ASSERT(m_ps->m_pTextReader == NULL);
    BFX_ASSERT(m_ps->m_nCharPos == 0 && m_ps->m_nCharsUsed == 0);

	m_ps->m_strBaseUri = strBaseUri;

	// allocate character buffer and fill its content with input string.
	m_ps->m_nCharsLength = str.GetLength();
	m_ps->m_pChars = new UCHAR[m_ps->m_nCharsLength + 1];
	memcpy(m_ps->m_pChars, str.GetData(), m_ps->m_nCharsLength * sizeof(UCHAR));
	m_ps->m_nCharsUsed = m_ps->m_nCharsLength;
	m_ps->m_pChars[m_ps->m_nCharsLength] = 0;	// \0

	m_ps->m_strEncoding = strOriginalEncoding;
	m_ps->m_bIsEof = true;
}

//
// Parse methods
//
// Parses the XML or text declaration and switched encoding if needed.
bool REFXMLTextReaderImpl::ParseXmlDeclaration(bool bIsTextDecl)
{
	// parse version, encoding & standalone attributes
	int nXmlDeclState = 0; // <?xml (0) version='1.0' (1) encoding='__' (2) standalone='__' (3) ?>
	UniString strEncoding;
	UniString strPrefix;
	UniString strAttrName;

	// parsing of text declarations cannot change global m_pStringBuidler or m_pCurNode as we may
	// be in the middle of a text node
	BFX_ASSERT(m_pStringBuilder->GetLength() == 0 || bIsTextDecl);
	REF<REFUniStringBuilder> sb = bIsTextDecl ? new REFUniStringBuilder() : (REFUniStringBuilder*)m_pStringBuilder;

	while (m_ps->m_nCharsUsed - m_ps->m_nCharPos < 6) // minimum "<?xml "
	{
		if (ReadData() == 0)
			goto NoXmlDecl;
	}

	strPrefix = UniString(m_ps->m_pChars + m_ps->m_nCharPos, 5);
	if (strPrefix != USTR("<?xml") || XMLCharType::IsNameChar(m_ps->m_pChars[m_ps->m_nCharPos + 5]))
	{
		goto NoXmlDecl;
	}

	if (!bIsTextDecl)
	{
		m_pCurNode->SetLineInfo(m_ps->m_nLineNo, m_ps->GetLinePos() + 2);
		m_pCurNode->SetNamedNode(XMLNodeType_XmlDeclaration, m_strXml);
	}
	m_ps->m_nCharPos += 5;

	for (;;)
	{
		int nPos;
		UCHAR quoteChar;
		NodeData* pAttr;
		int nNameEndPos;

		int nOriginalSbLen = sb->GetLength();
		int nWsCount = EatWhitespaces(nXmlDeclState == 0 ? NULL : sb);

		// end of xml declaration
		if (m_ps->m_pChars[m_ps->m_nCharPos] == '?')
		{
			sb->SetLength(nOriginalSbLen);
			if (m_ps->m_pChars[m_ps->m_nCharPos + 1] == '>')
			{
				if (nXmlDeclState == 0)
				{
					SetLastError(XML_InvalidXmlDecl);
					return false;
				}

				m_ps->m_nCharPos += 2;
				if (!bIsTextDecl)
				{
					m_pCurNode->SetValue(sb->ToString());
					sb->SetLength(0);

					m_nextParsingFunction = m_parsingFunction;
					m_parsingFunction = ParsingFunctionResetAttributesRootLevel;
				}

				// switch to encoding specified in xml declaration
				if (strEncoding.IsEmpty())
				{
					if (bIsTextDecl)
					{
						SetLastError(XML_InvalidTextDecl);
						return false;
					}

					// TODO: check for invalid encoding switches to default encoding
				}
				else
				{
					// TODO: SwitchEncoding(strEncoding);
				}
				m_ps->m_bAppendMode = false;
				return true;
			}
			else if (m_ps->m_nCharPos + 1 == m_ps->m_nCharsUsed)
			{
				goto ReadData;
			}
			else
			{
				SetLastError(XML_UnexpectedToken, ">");
				return false;
			}
		}
		if (nWsCount == 0 && nXmlDeclState != 0)
		{
			SetLastError(XML_UnexpectedToken, "?>");
			return false;
		}

		// read attribute name
		nNameEndPos = ParseName();
		if (nNameEndPos == -1)
			return false;

		pAttr = NULL;

		strAttrName = UniString(m_ps->m_pChars + m_ps->m_nCharPos, nNameEndPos - m_ps->m_nCharPos);
		switch (m_ps->m_pChars[m_ps->m_nCharPos])
		{
		case 'v':
			if (strAttrName == USTR("version") && nXmlDeclState == 0)
			{
				if (!bIsTextDecl)
				{
					pAttr = AddAttributeNoChecks(strAttrName, 0);
				}
				break;
			}
			/* no break */
		case 'e':
			if (strAttrName == USTR("encoding") && (nXmlDeclState == 1 || (bIsTextDecl && nXmlDeclState == 0)))
			{
				if (!bIsTextDecl)
				{
					pAttr = AddAttributeNoChecks(strAttrName, 0);
				}
				nXmlDeclState = 1;
				break;
			}
			/* no break */
		case 's':
			if (strAttrName == USTR("standalone") && (nXmlDeclState == 1 || nXmlDeclState == 2)
					&& !bIsTextDecl)
			{
				if (!bIsTextDecl)
				{
					pAttr = AddAttributeNoChecks(strAttrName, 0);
				}
				nXmlDeclState = 2;
				break;
			}
			/* no break */
		default:
			SetLastError(XML_InvalidXmlDecl);
			return false;
		}
		if (!bIsTextDecl)
		{
			pAttr->SetLineInfo(m_ps->m_nLineNo, m_ps->GetLinePos());
		}
		sb->Append(m_ps->m_pChars, m_ps->m_nCharPos, nNameEndPos - m_ps->m_nCharPos);
		m_ps->m_nCharPos = nNameEndPos;

		// parse equals and quote char;
		if (m_ps->m_pChars[m_ps->m_nCharPos] != '=')
		{
			EatWhitespaces(sb);
			if (m_ps->m_pChars[m_ps->m_nCharPos] != '=')
			{
				SetLastError(XML_UnexpectedToken, "=");
				return false;
			}
		}
		sb->Append('=');
		m_ps->m_nCharPos++;

		quoteChar = m_ps->m_pChars[m_ps->m_nCharPos];
		if (quoteChar != '"' && quoteChar != '\'')
		{
			EatWhitespaces(sb);
			quoteChar = m_ps->m_pChars[m_ps->m_nCharPos];
			if (quoteChar != '"' && quoteChar != '\'')
			{
				SetLastError(XML_UnexpectedToken, "\"");
				return false;
			}
		}
		sb->Append(quoteChar);
		m_ps->m_nCharPos++;
		if (!bIsTextDecl)
		{
			pAttr->m_quoteChar = quoteChar;
			pAttr->SetLineInfo2(m_ps->m_nLineNo, m_ps->GetLinePos());
		}

		// parse attribute value
		nPos = m_ps->m_nCharPos;
		UCHAR* pChars;
Continue:
		pChars = m_ps->m_pChars;
		while (XMLCharType::IsAttributeValueChar(pChars[nPos]))
		{
			nPos++;
		}

		if (m_ps->m_pChars[nPos] == quoteChar)
		{
			UniString strAttrVal = UniString(m_ps->m_pChars + m_ps->m_nCharPos, nPos - m_ps->m_nCharPos);
			switch (nXmlDeclState)
			{
			// version
			case 0:
				if (strAttrVal == USTR("1.0"))
				{
					if (!bIsTextDecl)
					{
						pAttr->SetValue(strAttrVal);
					}
					nXmlDeclState = 1;
				}
				else
				{
					SetLastError(XML_InvalidVersionNumber);
					return false;
				}
				break;
			case 1:
				// check encoding
				// TODO: strEncoding = CheckEncoding(strAttrVal);
				if (!bIsTextDecl)
				{
					pAttr->SetValue(strAttrVal);
				}
				nXmlDeclState = 2;
				break;
			case 2:
				if (strAttrVal == USTR("yes"))
				{
					m_bStandalone = true;
				}
				else if (strAttrVal == USTR("no"))
				{
					m_bStandalone = false;
				}
				else
				{
					BFX_ASSERT(!bIsTextDecl);
					SetLastError(XML_InvalidXmlDecl);
					return false;
				}
				if (!bIsTextDecl)
				{
					pAttr->SetValue(strAttrVal);
				}
				nXmlDeclState = 3;
				break;
			default:
				BFX_ASSERT(false); // NOTE: Should never run at this point.
				break;
			}
			sb->Append(pChars, m_ps->m_nCharPos, nPos - m_ps->m_nCharPos);
			sb->Append(quoteChar);
			m_ps->m_nCharPos = nPos + 1;
			continue;
		}
		else if (nPos == m_ps->m_nCharsUsed)
		{
			if (ReadData() != 0)
			{
				goto Continue;
			}
			else
			{
				SetLastError(XML_UnclosedQuote);
				return false;
			}
		}
		else
		{
			SetLastError(bIsTextDecl ? XML_InvalidTextDecl : XML_InvalidXmlDecl);
			return false;
		}

ReadData:
		if (m_ps->m_bIsEof || ReadData() == 0)
		{
			SetLastError(XML_UnexpectedEOF);
			return false;
		}
	}

NoXmlDecl:
	// no xml declaration
	if (!bIsTextDecl)
	{
		m_parsingFunction = m_nextParsingFunction;
	}
	// TODO: check for invalid encoding switches to default encoding
	m_ps->m_bAppendMode = false;
	return false;
}

// Parses the document content
bool REFXMLTextReaderImpl::ParseDocumentContent()
{
	while (!IsErrorOccurred())
	{
		bool bNeedMoreChars = false;
		int nPos = m_ps->m_nCharPos;
		UCHAR* pChars = m_ps->m_pChars;

		// some tag
		if (pChars[nPos] == '<')
		{
			bNeedMoreChars = true;
			if (m_ps->m_nCharsUsed - nPos < 4) // minimum  "<a/>"
				goto ReadData;
			nPos++;
			switch (pChars[nPos])
			{
				// processing instruction
			case '?':
				m_ps->m_nCharPos = nPos + 1;
				if (ParsePI())
				{
					return true;
				}
				continue;
			case '!':
				nPos++;
				if (m_ps->m_nCharsUsed - nPos < 2)	// minimum characters expected "--"
					goto ReadData;
				// comment
				if (pChars[nPos] == '-')
				{
					if (pChars[nPos+1] == '-')
					{
						m_ps->m_nCharPos = nPos + 2;
						if (ParseComment())
						{
							return true;
						}
						continue;
					}
					else
					{
						SetLastError(XML_UnexpectedToken, "-");
						return false;
					}
				}
				// CDATA section
				else if (pChars[nPos] == '[')
				{
					if (m_fragmentType != XMLNodeType_Document)
					{
						nPos++;
						if (m_ps->m_nCharsUsed - nPos < 6)
						{
							goto ReadData;
						}
						UniString str(pChars + nPos, 6);
						if (str == USTR("CDATA["))
						{
							m_ps->m_nCharPos = nPos + 6;
							if (!ParseCData())
							{
								continue;
							}
							if (m_fragmentType == XMLNodeType_None)
							{
								m_fragmentType = XMLNodeType_Element;
							}
							return true;
						}
						else
						{
							SetLastError(XML_UnexpectedToken, "CDATA[");
							return false;
						}
					}
					else
					{
						SetLastError(XML_InvalidRootData);
						return false;
					}
				}
				// DOCTYPE declaration
				else
				{
					if (m_fragmentType == XMLNodeType_Document || m_fragmentType == XMLNodeType_None)
					{
						m_fragmentType = XMLNodeType_Document;
						m_ps->m_nCharPos = nPos;
						return ParseDoctypeDecl();
					}
					else
					{
						// convert utf-16 to ansi charset.
						UniString str = ParseUnexpectedToken(nPos);
						SetLastError(XML_UnexpectedToken, CSTR(str));
						return false;
					}
				}
				break;
			case '/':
				SetLastError(XML_UnexpectedEndTag);
				return false;
				// document element start tag
			default:
				if (m_bRootElementParsed)
				{
					if (m_fragmentType == XMLNodeType_Document)
					{
						SetLastError(XML_MultipleRoots);
						return false;
					}
					if (m_fragmentType == XMLNodeType_None)
					{
						m_fragmentType = XMLNodeType_Element;
					}
				}
				m_ps->m_nCharPos = nPos;
				m_bRootElementParsed = true;
				return ParseElement();
			}
		}
		else if (pChars[nPos] == '&')
		{
			if (m_fragmentType == XMLNodeType_Document)
			{
				SetLastError(XML_InvalidRootData, nPos);
				return false;
			}
			else
			{
				if (m_fragmentType == XMLNodeType_None)
				{
					m_fragmentType = XMLNodeType_Element;
				}
				int i;
				switch (HandleEntityReference(false, EntityExpandTypeOnlyGeneral, OUT i))
				{
				case EntityTypeUnexpanded:
					if (m_parsingFunction == ParsingFunctionEntityReference)
					{
						m_parsingFunction = m_nextParsingFunction;
					}
					return ParseEntityReference();
				case EntityTypeCharacterDec:
				case EntityTypeCharacterHex:
				case EntityTypeCharacterNamed:
					if (ParseText())
					{
						return true;
					}
					continue;
				default:
					if (IsErrorOccurred())
						return false;
					pChars = m_ps->m_pChars;
					nPos = m_ps->m_nCharPos;
					continue;
				}
			}
		}
		// end of buffer
		else if ((nPos == m_ps->m_nCharsUsed) || (pChars[nPos] == 0x00))
		{
			goto ReadData;
		}
		// something else -> root level whitespaces
		else
		{
			if (m_fragmentType == XMLNodeType_Document)
			{
				if (ParseRootLevelWhitespace())
				{
					return true;
				}
				if (IsErrorOccurred())
					return false;
			}
			else
			{
				if (ParseText())
				{
					if (m_fragmentType == XMLNodeType_None && m_pCurNode->m_type == XMLNodeType_Text)
					{
						m_fragmentType = XMLNodeType_Element;
					}
					return true;
				}
				if (IsErrorOccurred())
					return false;
			}
			pChars = m_ps->m_pChars;
			nPos = m_ps->m_nCharPos;
			continue;
		}

		BFX_ASSERT(nPos == m_ps->m_nCharsUsed && !m_ps->m_bIsEof);

ReadData:
		// read new characters into the buffer
		if (ReadData() != 0)
		{
			nPos = m_ps->m_nCharPos;
		}
		else
		{
			if (bNeedMoreChars)
			{
				SetLastError(XML_InvalidRootData);
				return false;
			}

			if (IsInEntity())
			{
				if (HandleEntityEnd(true))
				{
					// NOTE: HandleEntityEnd return true means entity resolved manually! the node should be reported.

					// TODO: SetupEndEntityNodeInContent
					BFX_ASSERT(false);
					return true;
				}
				continue;
			}
			BFX_ASSERT(m_nCurNodeIndex == 0);

			if (!m_bRootElementParsed && m_fragmentType == XMLNodeType_Document)
			{
				SetLastError(XML_MissingRoot);
				return false;
			}
			if (m_fragmentType == XMLNodeType_None)
			{
				m_fragmentType = m_bRootElementParsed ? XMLNodeType_Document : XMLNodeType_Element;
			}
			OnEof();
			return false;
		}

		nPos = m_ps->m_nCharPos;
		pChars = m_ps->m_pChars;
	}

	return false;
}

// Parses element content
bool REFXMLTextReaderImpl::ParseElementContent()
{
	while (!IsErrorOccurred())
	{
		int nPos = m_ps->m_nCharPos;
		UCHAR* pChars = m_ps->m_pChars;

		switch (pChars[nPos])
		{
			// some tag
		case '<':
			switch (pChars[nPos+1])
			{
				// processing instruction
			case '?':
				m_ps->m_nCharPos = nPos + 2;
				if (ParsePI())
				{
					return true;
				}
				continue;
			case '!':
				nPos += 2;
				if (m_ps->m_nCharsUsed - nPos < 2)
					goto ReadData;
				// comment
				if (pChars[nPos] == '-')
				{
					if (pChars[nPos+1] == '-')
					{
						m_ps->m_nCharPos = nPos + 2;
						if (ParseComment())
						{
							return true;
						}
						continue;
					}
					else
					{
						SetLastError(XML_UnexpectedToken/*, '-', nPos + 1*/);
						return false;
					}
				}
				// CDATA section
				else if (pChars[nPos] == '[')
				{
					nPos++;
					if (m_ps->m_nCharsUsed - nPos < 6)
					{
						goto ReadData;
					}
					if (UniString(pChars + nPos, 6) == USTR("CDATA["))
					{
						m_ps->m_nCharPos = nPos + 6;
						return ParseCData();
					}
					else
					{
						SetLastError(XML_UnexpectedToken/*, "CDATA[", nPos*/);
						return false;
					}
				}
				else
				{
					// NOTE: Is it necessary???
					if (ParseUnexpectedToken(nPos) == USTR("DOCTYPE"))
					{
						SetLastError(XML_BadDTDLocation);
					}
					else
					{
						SetLastError(XML_UnexpectedToken, "<[CDATA[");
					}
					return false;
				}
				break;
				// element end tag
			case '/':
				m_ps->m_nCharPos = nPos + 2;
				return ParseEndElement();
			default:
				// end of buffer
				if (nPos + 1 == m_ps->m_nCharsUsed)
				{
					goto ReadData;
				}
				else
				{
					// element start tag
					m_ps->m_nCharPos = nPos + 1;
					return ParseElement();
				}
			}
			break;
		case '&':
			if (ParseText())
			{
				return true;
			}
			continue;
		default:
			// end of buffer
			if (nPos == m_ps->m_nCharsUsed)
			{
				goto ReadData;
			}
			else
			{
				// text node, whitespace or entity reference
				if (ParseText())
				{
					return true;
				}
				continue;
			}
		}

ReadData:
		// read new characters into the buffer
		if (ReadData() == 0)
		{
			if (m_ps->m_nCharsUsed - m_ps->m_nCharPos != 0)
			{
				SetLastError(XML_UnexpectedEOFInElementContent);
				return false;
			}
			if (!IsInEntity())
			{
				if (m_nCurNodeIndex == 0 && m_fragmentType != XMLNodeType_Document)
				{
					OnEof();
					return false;
				}
				SetLastError(XML_UnexpectedEOFInElementContent);
				return false;
			}
			if (HandleEntityEnd(true))
			{
				// TODO: SetupEndEntityNodeInContent();
				BFX_ASSERT (false);
			}
		}
	}
	return false;
}

// Parses the element end tag
bool REFXMLTextReaderImpl::ParseEndElement()
{
	// check if the end tag name equals start tag name
	NodeData* pStartTagNode = m_pNodes[m_nCurNodeIndex-1];

	int nPrefLen = pStartTagNode->m_strPrefix.GetLength();
	int nLocLen = pStartTagNode->m_strLocalName.GetLength();

	while (m_ps->m_nCharsUsed - m_ps->m_nCharPos < nPrefLen + nLocLen + 1)
	{
		if (ReadData() == 0)
		{
			break;
		}
	}

	int nNameLen;
	UCHAR* pChars = m_ps->m_pChars;
	if (pStartTagNode->m_strPrefix.GetLength() == 0)
	{
		if (UniString(pChars + m_ps->m_nCharPos, nLocLen) != pStartTagNode->m_strLocalName)
		{
			SetLastErrorTagMismatch(pStartTagNode);
			return false;
		}
		nNameLen = nLocLen;
	}
	else
	{
		int nColonPos = m_ps->m_nCharPos + nPrefLen;
		if (UniString(pChars + m_ps->m_nCharPos, nPrefLen) != pStartTagNode->m_strPrefix ||
			pChars[nColonPos] != ':' ||
			UniString(pChars + nColonPos + 1, nLocLen) != pStartTagNode->m_strLocalName)
		{
			SetLastErrorTagMismatch(pStartTagNode);
			return false;
		}
		nNameLen = nLocLen + nPrefLen + 1;
	}

	int nPos;
	for (;;)
	{
		nPos = m_ps->m_nCharPos + nNameLen;
		pChars = m_ps->m_pChars;

		if (nPos == m_ps->m_nCharsUsed)
		{
			goto ReadData;
		}
		if (XMLCharType::IsNCNameChar(pChars[nPos]) || pChars[nPos] == ':')
		{
			SetLastErrorTagMismatch(pStartTagNode);
			return false;
		}

		// eat whitespaces
		while (XMLCharType::IsWhiteSpace(pChars[nPos]))
		{
			nPos ++;
		}

		if (pChars[nPos] == '>')
		{
			break;
		}
		else if (nPos == m_ps->m_nCharsUsed)
		{
			goto ReadData;
		}
		else
		{
			SetLastError(XML_UnexpectedToken, ">", nPos);
			return false;
		}

		BFX_ASSERT(false && "We should never get to this point.");

ReadData:
		if (ReadData() == 0)
		{
			SetLastError(XML_UnexpectedEOFInElementContent, CSTR(m_pStringBuilder->ToString()));
			return false;
		}
	}

	BFX_ASSERT(m_nCurNodeIndex > 0);
	m_pCurNode = m_pNodes[-- m_nCurNodeIndex];

	// set the element data
	BFX_ASSERT(m_pCurNode == pStartTagNode);
	pStartTagNode->SetLineInfo(m_ps->m_nLineNo, m_ps->GetLinePos());
	pStartTagNode->m_type = XMLNodeType_EndElement;

	m_ps->m_nCharPos = nPos + 1;

	// set next parsing function
	m_nextParsingFunction = (m_nCurNodeIndex > 0) ? m_parsingFunction : ParsingFunctionDocumentContent;
	m_parsingFunction = ParsingFunctionPopElementContext;

	return true;
}

void REFXMLTextReaderImpl::SetLastErrorTagMismatch(NodeData* pStartTag)
{
	if (pStartTag->m_type == XMLNodeType_Element)
	{
		// parse the bad name
		int nColonPos;
		/*int nEndPos = */ParseQName(OUT nColonPos);

		SetLastError(XML_TagMismatch, CSTR(pStartTag->m_strNameWPrefix), pStartTag->m_lineInfo.m_nLineNo, pStartTag->m_lineInfo.m_nLinePos);
	}
	else
	{
		BFX_ASSERT(pStartTag->m_type == XMLNodeType_EntityReference);
		SetLastError(XML_UnexpectedEndTag);
	}
}

// Parses processing instruction. if pPiInDtdStringBuilder != NULL, the processing instruction
// is in DTD and it will be saved in the passed string builder (target, whitespace & value).
bool REFXMLTextReaderImpl::ParsePI(REFUniStringBuilder* pPiInDtdStringBuilder)
{
	if (m_parsingMode == ParsingModeFull)
	{
		m_pCurNode->SetLineInfo(m_ps->m_nLineNo, m_ps->GetLinePos());
	}

	BFX_ASSERT(m_pStringBuilder->GetLength() == 0);

	// parse target name
	int nNameEndPos = ParseName();
	UniString strTarget = m_pNameTable->Add(m_ps->m_pChars, m_ps->m_nCharPos, nNameEndPos - m_ps->m_nCharPos);

	if (strTarget.CompareTo(USTR("xml"), true) == 0)
	{
		SetLastError(strTarget == USTR("xml") ? XML_XmlDeclNotFirst : XML_InvalidPIName, CSTR(strTarget));
	}
	m_ps->m_nCharPos = nNameEndPos;

	if (pPiInDtdStringBuilder == NULL)
	{
		if (!m_bIgnorePIs && m_parsingMode == ParsingModeFull)
		{
			m_pCurNode->SetNamedNode(XMLNodeType_ProcessingInstruction, strTarget);
		}
	}
	else
	{
		pPiInDtdStringBuilder->Append(strTarget);
	}

	// check mandatory whitespace
	UCHAR ch = m_ps->m_pChars[m_ps->m_nCharPos];
	BFX_ASSERT(m_ps->m_nCharPos < m_ps->m_nCharsUsed);
	if (EatWhitespaces(pPiInDtdStringBuilder) == 0)
	{
		if (m_ps->m_nCharsUsed - m_ps->m_nCharPos < 2)
		{
			ReadData();
		}
		if (ch != '?' || m_ps->m_pChars[m_ps->m_nCharPos+1] != '>')
		{
			SetLastError(XML_BadNameChar, String((char)ch));
			return false;
		}
	}

	// scan processing instruction value
	int nStartPos, nEndPos;
	if (ParsePIValue(OUT nStartPos, OUT nEndPos))
	{
		if (pPiInDtdStringBuilder == NULL)
		{
			if (m_bIgnorePIs)
			{
				return false;
			}
			if (m_parsingMode == ParsingModeFull)
			{
				m_pCurNode->SetValue(UniString(m_ps->m_pChars + nStartPos, nEndPos - nStartPos));
			}
		}
		else
		{
			pPiInDtdStringBuilder->Append(m_ps->m_pChars, nStartPos, nEndPos - nStartPos);
		}
	}
	else
	{
		if (IsErrorOccurred())
			return false;

		REFUniStringBuilder* sb;
		if (pPiInDtdStringBuilder == NULL)
		{
			if (m_bIgnorePIs || m_parsingMode != ParsingModeFull)
			{
				while (!ParsePIValue(OUT nStartPos, OUT nEndPos))
				{
					if (IsErrorOccurred())
						return false;
				}
				return false;
			}
			sb = m_pStringBuilder;
			BFX_ASSERT(m_pStringBuilder->GetLength() == 0);
		}
		else
		{
			sb = pPiInDtdStringBuilder;
		}

		do
		{
			if (IsErrorOccurred())
				return false;
			sb->Append(m_ps->m_pChars, nStartPos, nEndPos - nStartPos);
		}
		while (!ParsePIValue(OUT nStartPos, OUT nEndPos));
		sb->Append(m_ps->m_pChars, nStartPos, nEndPos - nStartPos);

		if (pPiInDtdStringBuilder == NULL)
		{
			m_pCurNode->SetValue(m_pStringBuilder->ToString());
			m_pStringBuilder->SetLength(0);
		}
	}
	return true;
}

// Parses comment.
// Returns true if success, or false if skiped.
// in case of false, we need to check the error state by GetLastError().
bool REFXMLTextReaderImpl::ParseComment()
{
	if (m_bIgnoreComments)
	{
		ParsingMode oldParsingMode = m_parsingMode;
		m_parsingMode = ParsingModeSkipNode;
		ParseCDataOrComment(XMLNodeType_Comment);
		m_parsingMode = oldParsingMode;
		return false;
	}
	else
	{
		ParseCDataOrComment(XMLNodeType_Comment);
		return true;
	}
}

bool REFXMLTextReaderImpl::ParseCData()
{
	return ParseCDataOrComment(XMLNodeType_CDATA);
}

// Parses CDATA section or comment
bool REFXMLTextReaderImpl::ParseCDataOrComment(XMLNodeType type)
{
	int nStartPos, nEndPos;

	if (m_parsingMode == ParsingModeFull)
	{
		m_pCurNode->SetLineInfo(m_ps->m_nLineNo, m_ps->GetLinePos());
		BFX_ASSERT(m_pStringBuilder->GetLength() == 0);

		if (ParseCDataOrComment(type, OUT nStartPos, OUT nEndPos))
		{
			m_pCurNode->SetValueNode(type, UniString(m_ps->m_pChars + nStartPos, nEndPos - nStartPos));
		}
		else
		{
			if (IsErrorOccurred())
				return false;
			do
			{
				if (IsErrorOccurred())
					return false;
				m_pStringBuilder->Append(m_ps->m_pChars, nStartPos, nEndPos - nStartPos);
			}
			while (!ParseCDataOrComment(type, OUT nStartPos, OUT nEndPos));
			m_pStringBuilder->Append(m_ps->m_pChars, nStartPos, nEndPos - nStartPos);
			m_pCurNode->SetValueNode(type, m_pStringBuilder->ToString());
			m_pStringBuilder->SetLength(0);
		}
	}
	else
	{
		while (!ParseCDataOrComment(type, OUT nStartPos, OUT nEndPos))
		{
			if (IsErrorOccurred())
				return false;
		}
	}
	return true;
}

// Parses a chunk of CDATA section or comment. Returns true when the end of CDATA or comment was reached.
bool REFXMLTextReaderImpl::ParseCDataOrComment(XMLNodeType type, OUT int& nOutStartPos, OUT int& nOutEndPos)
{
	if (m_ps->m_nCharsUsed - m_ps->m_nCharPos < 3)
	{
		// read new characters into the buffer
		if (ReadData() == 0)
		{
			SetLastError(XML_UnexpectedEOF, (type == XMLNodeType_Comment) ? "Comment" : "CDATA");
			return false;
		}
	}

	int nPos = m_ps->m_nCharPos;
	UCHAR* pChars = m_ps->m_pChars;
	int rcount = 0;
	int rpos = -1;
	UCHAR stopChar = (type == XMLNodeType_Comment) ? '-' : ']';

	for (;;)
	{
		while (XMLCharType::IsTextChar(pChars[nPos]) && pChars[nPos] != stopChar)
			nPos ++;

		// posibbly end of comment or cdata section
		if (pChars[nPos] == stopChar)
		{
			if (pChars[nPos+1] == stopChar)
			{
				if (pChars[nPos+2] == '>')
				{
					if (rcount > 0)
					{
						ShiftBuffer(rpos + rcount, rpos, nPos - rpos - rcount);
						nOutEndPos = nPos - rcount;
					}
					else
					{
						nOutEndPos = nPos;
					}
					nOutStartPos = m_ps->m_nCharPos;
					m_ps->m_nCharPos = nPos + 3;
					return true;
				}
				else if (nPos+2 == m_ps->m_nCharsUsed)
				{
					goto ReturnPartial;
				}
				else if (type == XMLNodeType_Comment)
				{
					SetLastError(XML_InvalidCommentChars/*, nPos*/);
					return false;
				}
			}
			else if (nPos+1 == m_ps->m_nCharsUsed)
			{
				goto ReturnPartial;
			}
			nPos++;
			continue;
		}
		else {
			switch (pChars[nPos])
			{
				// eol
			case 0xA:
				nPos++;
				OnNewLine(nPos);
				continue;
			case 0xD:
				if (pChars[nPos+1] == 0xA)
				{
					// EOL normalization of 0xD 0xA - shift the buffer
					if (m_parsingMode == ParsingModeFull)
					{
						if (nPos - m_ps->m_nCharPos > 0)
						{
							if (rcount == 0)
							{ 
								rcount = 1;
								rpos = nPos;
							}
							else
							{
								ShiftBuffer(rpos + rcount, rpos, nPos - rpos - rcount);
								rpos = nPos - rcount;
								rcount++;
							}
						}
						else
						{
							m_ps->m_nCharPos++;
						}
					}
					nPos += 2;
				}
				else if (nPos+1 < m_ps->m_nCharsUsed || m_ps->m_bIsEof)
				{
					pChars[nPos] = 0xA;             // EOL normalization of 0xD
					nPos++;
				}
				else
				{
					goto ReturnPartial;
				}
				OnNewLine(nPos);
				continue;
			case '<':
			case '&':
			case ']':
			case 0x9:
				nPos++;
				continue;
			default:
				// end of buffer
				if (nPos == m_ps->m_nCharsUsed)
				{
					goto ReturnPartial;
				}
				// surrogate characters
				UCHAR ch = pChars[nPos];
				if (CharUnicodeInfo::IsHighSurrogate(ch))
				{
					if (nPos + 1 == m_ps->m_nCharsUsed)
					{
						goto ReturnPartial;
					}
					nPos++;
					if (CharUnicodeInfo::IsLowSurrogate(pChars[nPos]))
					{
						nPos++;
						continue;
					}
				}
				SetLastError(XML_InvalidCharacter/*, ch, nPos*/);
				return false;
				break;
			}
		}

ReturnPartial:
		if (rcount > 0)
		{
			ShiftBuffer(rpos + rcount, rpos, nPos - rpos - rcount);
			nOutEndPos = nPos - rcount;
		}
		else
		{
			nOutEndPos = nPos;
		}
		nOutStartPos = m_ps->m_nCharPos;

		m_ps->m_nCharPos = nPos;
		return false; // false == parsing of comment or CData section is not finished yet, must be called again
	}
	BFX_ASSERT1(false, "should never run at this point.");
	return false;
}

// Parses the element start tag
bool REFXMLTextReaderImpl::ParseElement()
{
	int nPos = m_ps->m_nCharPos;
	UCHAR* pChars = m_ps->m_pChars;
	int nColonPos = -1;

	m_pCurNode->SetLineInfo(m_ps->m_nLineNo, m_ps->GetLinePos());

	// PERF: we intentionally don't call ParseQName here to parse the element name unless a special 
	// case occurs (like end of buffer, invalid name char)
ContinueStartName:
	// check element name start char
	if (!XMLCharType::IsStartNCNameChar(pChars[nPos]))
	{
		goto ParseQNameSlow;
	}
	nPos++;

ContinueName:
	// parse element name
	while (XMLCharType::IsNCNameChar(pChars[nPos]))
	{
		nPos++;
	}

	// colon -> save prefix end position and check next char if it's name start char
	if (pChars[nPos] == ':')
	{
		if (nColonPos != -1)
		{
			if (!m_bSupportNamespaces)
			{
				SetLastError(XML_BadNameChar, ':', nPos);
				return false;
			}
			nPos++;
			goto ContinueName;
		}
		else
		{
			nColonPos = nPos;
			nPos++;
			goto ContinueStartName;
		}
	}
	else if (nPos < m_ps->m_nCharsUsed)
	{
		goto SetElement;
	}

ParseQNameSlow:
	nPos = ParseQName(OUT nColonPos);
	if (IsErrorOccurred())	// nPos == -1 means error occurs, maybe unexpected character.
		return false;

	pChars = m_ps->m_pChars;

SetElement:
	// push namespace context
	m_pNamespaceManager->PushScope();

	// init the NodeData class
	if (nColonPos == -1 || m_bSupportNamespaces)
	{
		m_pCurNode->SetNamedNode(XMLNodeType_Element, m_pNameTable->Add(pChars, m_ps->m_nCharPos, nPos - m_ps->m_nCharPos));
	}
	else
	{
		int nStartPos = m_ps->m_nCharPos;
		int nPrefixLen = nColonPos - nStartPos;
		UniString strPrefix(pChars + nStartPos, nPrefixLen);
		if (nPrefixLen == m_strLastPrefix.GetLength() && strPrefix == m_strLastPrefix)
		{
			m_pCurNode->SetNamedNode(XMLNodeType_Element, m_pNameTable->Add(pChars, nColonPos + 1, nPos - nColonPos - 1),
				m_strLastPrefix, UniString());
		}
		else
		{
			m_pCurNode->SetNamedNode(XMLNodeType_Element,
					m_pNameTable->Add(pChars, nColonPos + 1, nPos - nColonPos - 1),
					m_pNameTable->Add(pChars, m_ps->m_nCharPos, nPrefixLen), UniString());
			m_strLastPrefix = m_pCurNode->m_strPrefix;
		}
	}

	UCHAR ch = pChars[nPos];
	// white space after element name -> there are probably some attributes
	bool bIsWs = XMLCharType::IsWhiteSpace(ch);

	if (bIsWs)
	{
		m_ps->m_nCharPos = nPos;
		return ParseAttributes();
	}
	// no attributes
	else
	{
		// non-empty element
		if (ch == '>')
		{
			m_ps->m_nCharPos = nPos + 1;
			m_parsingFunction = ParsingFunctionMoveToElementContent;
		}
		// empty element
		else if (ch == '/')
		{
			if (nPos + 1 == m_ps->m_nCharsUsed)
			{
				m_ps->m_nCharPos = nPos;
				if (ReadData() == 0)
				{
					SetLastError(XML_UnexpectedEOF, ">", nPos);
					return false;
				}
				nPos = m_ps->m_nCharPos;
				pChars = m_ps->m_pChars;
			}
			if (pChars[nPos + 1] == '>')
			{
				m_pCurNode->SetEmptyElement(true);
				m_nextParsingFunction = m_parsingFunction;
				m_parsingFunction = ParsingFunctionPopEmptyElementContext;
				m_ps->m_nCharPos = nPos + 2;
			}
			else
			{
				SetLastError(XML_UnexpectedToken, ">", nPos);
				return false;
			}
		}
		// something else after the element name
		else
		{
			SetLastError(XML_BadNameChar, (char)ch, nPos);
			return false;
		}

		// TODO: add default attributes & strip spaces in attributes with type other than CDATA
		// lookup element namespace
		ElementNamespaceLookup();
	}

	return true;
}

UniString REFXMLTextReaderImpl::ParseUnexpectedToken(int nNewPos)
{
	if (nNewPos >= 0)
	{
		m_ps->m_nCharPos = nNewPos;
	}
	if (XMLCharType::IsNCNameChar(m_ps->m_pChars[m_ps->m_nCharPos]))
	{
		int nPos = m_ps->m_nCharPos + 1;
		while (XMLCharType::IsNCNameChar(m_ps->m_pChars[nPos]))
		{
			nPos ++;
		}
		return UniString(m_ps->m_pChars+ m_ps->m_nCharPos, nPos - m_ps->m_nCharPos);
	}
	else
	{
		BFX_ASSERT(m_ps->m_nCharPos < m_ps->m_nCharsUsed);
		return UniString(m_ps->m_pChars + m_ps->m_nCharPos, 1);
	}
}

bool REFXMLTextReaderImpl::ParseRootLevelWhitespace()
{
	BFX_ASSERT(m_pStringBuilder->GetLength() == 0);

	XMLNodeType nodeType = GetWhitespaceType();

	if (nodeType == XMLNodeType_None)
	{
		EatWhitespaces(NULL);
		// TODO: ZeroEndingStream
		if (m_ps->m_pChars[m_ps->m_nCharPos] == '<' || m_ps->m_nCharsUsed - m_ps->m_nCharPos == 0)
		{
			return false;
		}
	}
	else
	{
		m_pCurNode->SetLineInfo(m_ps->m_nLineNo, m_ps->GetLinePos()); 
		EatWhitespaces(m_pStringBuilder);
		// TODO: ZeroEndingStream
		if (m_ps->m_pChars[m_ps->m_nCharPos] == '<' || m_ps->m_nCharsUsed - m_ps->m_nCharPos == 0)
		{
			if (m_pStringBuilder->GetLength() > 0)
			{
				m_pCurNode->SetValueNode(nodeType, m_pStringBuilder->ToString());
				m_pStringBuilder->SetLength(0);
				return true;
			}
			return false;
		}
	}

	if (XMLCharType::IsCharData(m_ps->m_pChars[m_ps->m_nCharPos]))
	{
		SetLastError(XML_InvalidRootData);
	}
	else
	{
		SetLastError(XML_InvalidCharacter, m_ps->m_pChars[m_ps->m_nCharPos], m_ps->m_nCharPos);
	}
	return false;
}

// Parses text or white space node.
// Returns true if a node has been parsed and its data set to curNode. 
// Returns false when a white space has been parsed and ignored (according to current whitespace handling) or when parsing mode is not Full.
// Also returns false if there is no text to be parsed.
bool REFXMLTextReaderImpl::ParseText()
{
	int nStartPos;
	int nEndPos;
	int orChars = 0;

	// skip over the text if not in full parsing mode
	if (m_parsingMode != ParsingModeFull)
	{
		while (!ParseText(OUT nStartPos, OUT nEndPos, INOUT orChars))
		{
			// NOTE: in case of returns false, dont forget to check the error state.
			if (IsErrorOccurred())
				return false;
		}
		goto IgnoredNode;
	}

	m_pCurNode->SetLineInfo(m_ps->m_nLineNo, m_ps->GetLinePos()); 
	BFX_ASSERT(m_pStringBuilder->GetLength() == 0);

	// the whole value is in buffer
	if (ParseText(OUT nStartPos, OUT nEndPos, INOUT orChars))
	{
		if (nEndPos - nStartPos == 0)
		{
			goto IgnoredNode;
		}
		XMLNodeType nodeType = GetTextNodeType(orChars);
		if (nodeType == XMLNodeType_None)
		{
			goto IgnoredNode;
		}
		BFX_ASSERT(nEndPos - nStartPos > 0);
		m_pCurNode->SetValueNode(nodeType, UniString(m_ps->m_pChars + nStartPos, nEndPos - nStartPos));
		return true;
	}
	// only piece of the value was returned
	else
	{
		if (m_bV1Compat)
		{
			// V1 compatibility mode -> cache the whole value
			do
			{
				// NOTE: in case of returns false, dont forget to check the error state.
				if (IsErrorOccurred())
					return false;
				m_pStringBuilder->Append(m_ps->m_pChars, nStartPos, nEndPos - nStartPos);
			}
			while (!ParseText(OUT nStartPos, OUT nEndPos, INOUT orChars));
			m_pStringBuilder->Append(m_ps->m_pChars, nStartPos, nEndPos - nStartPos);

			BFX_ASSERT(m_pStringBuilder->GetLength() > 0);

			XMLNodeType nodeType = GetTextNodeType(orChars);
			if (nodeType == XMLNodeType_None)
			{
				m_pStringBuilder->SetLength(0);
				goto IgnoredNode;
			}

			m_pCurNode->SetValueNode(nodeType, m_pStringBuilder->ToString());
			m_pStringBuilder->SetLength(0);
			return true;
		}
		else
		{
			// V2 reader -> do not cache the whole value yet, read only up to 4kB to decide whether the value is a whitespace
			const int MinWhitespaceLookahedCount = 4096;
			bool bFullValue = false;

			// NOTE: in case of returns false, dont forget to check the error state.
			if (IsErrorOccurred())
				return false;

			// if it's a partial text value, not a whitespace -> return
			if (orChars > 0x20)
			{
				BFX_ASSERT(nEndPos - nStartPos > 0);
				m_pCurNode->SetValueNode(XMLNodeType_Text, UniString(m_ps->m_pChars + nStartPos, nEndPos - nStartPos));
				m_nextParsingFunction = m_parsingFunction;
				m_parsingFunction = ParsingFunctionPartialTextValue;
				return true;
			}

			// partial whitespace -> read more data (up to 4kB) to decide if it is a whitespace or a text node
			m_pStringBuilder->Append(m_ps->m_pChars, nStartPos, nEndPos - nStartPos);
			do
			{
				bFullValue = ParseText(OUT nStartPos, OUT nEndPos, INOUT orChars);

				// NOTE: in case of returns false, dont forget to check the error state.
				if (!bFullValue && IsErrorOccurred())
					return false;

				m_pStringBuilder->Append(m_ps->m_pChars, nStartPos, nEndPos - nStartPos);
			} while (!bFullValue && orChars <= 0x20 && m_pStringBuilder->GetLength() < MinWhitespaceLookahedCount);

			// determine the value node type
			XMLNodeType nodeType = (m_pStringBuilder->GetLength() < MinWhitespaceLookahedCount) ? GetTextNodeType(orChars) : XMLNodeType_Text;
			if (nodeType == XMLNodeType_None)
			{
				// ignored whitespace -> skip over the rest of the value unless we already read it all
				m_pStringBuilder->SetLength(0);
				if (!bFullValue)
				{
					while (!ParseText(OUT nStartPos, OUT nEndPos, INOUT orChars))
					{
						if (IsErrorOccurred())
							return false;
					}
				}
				goto IgnoredNode;
			}
			// set value to curNode
			m_pCurNode->SetValueNode(nodeType, m_pStringBuilder->ToString());
			m_pStringBuilder->SetLength(0);

			// change parsing state if the full value was not parsed
			if (!bFullValue)
			{
				m_nextParsingFunction = m_parsingFunction;
				m_parsingFunction = ParsingFunctionPartialTextValue;
			}
			return true;
		}
	}

IgnoredNode:
	// ignored whitespace at the end of manually resolved entity
	if (m_parsingFunction == ParsingFunctionReportEndEntity)
	{
		// TODO: SetupEndEntityNodeInContent();
		m_parsingFunction = m_nextParsingFunction;
		BFX_ASSERT(false);
		return true;
	}
	else if (m_parsingFunction == ParsingFunctionEntityReference)
	{
		m_parsingFunction = m_nextNextParsingFunction;
		ParseEntityReference();
		return true;
	}
	return false;
}

// Parses a chunk of text starting at m_ps->nCharPos.
//   nStartPos .... start position of the text chunk that has been parsed (can differ from m_ps->nCharPos before the call)
//   nEndPos ...... end position of the text chunk that has been parsed (can differ from m_ps->nCharPos after the call)
//   outOrChars .. all parsed character bigger or equal to 0x20 or-ed (|) into a single int. It can be used for whitespace detection 
//                 (the text has a non-whitespace character if outOrChars > 0x20).
// Returns true when the whole value has been parsed. Return false when it needs to be called again to get a next chunk of value.
// NOTE: In case of false we should call GetLastError() to check the error state.
bool REFXMLTextReaderImpl::ParseText(OUT int& nStartPos, OUT int& nEndPos, INOUT int& outOrChars)
{
	UCHAR* pChars = m_ps->m_pChars;
	int nPos = m_ps->m_nCharPos;
	int rcount = 0;
	int rpos = -1;
	int orChars = outOrChars;
	UCHAR c;

	for (;;) {
		// parse text content
		while (XMLCharType::IsTextChar(c = pChars[nPos]))
		{
			orChars |= (int)c;
			nPos++;
		}

		switch (c)
		{
		case 0x9:
			nPos++;
			continue;
			// eol
		case 0xA:
			nPos++;
			OnNewLine(nPos);
			continue;
		case 0xD:
			if (pChars[nPos+1] == 0xA)
			{
				if (m_parsingMode == ParsingModeFull)
				{
					if (nPos - m_ps->m_nCharPos > 0)
					{
						if (rcount == 0)
						{
							rcount = 1;
							rpos = nPos;
						}
						else
						{
							ShiftBuffer(rpos + rcount, rpos, nPos - rpos - rcount);
							rpos = nPos - rcount;
							rcount++;
						}
					}
					else
					{
						m_ps->m_nCharPos++;
					}
				}
				nPos += 2;
			}
			else if (nPos + 1 < m_ps->m_nCharsUsed || m_ps->m_bIsEof)
			{
				pChars[nPos] = 0xA;		// EOL normalization of 0xD
				nPos++;
			}
			else
			{
				goto ReadData;
			}
			OnNewLine(nPos);
			continue;
			// some tag 
		case '<':
			goto ReturnPartialValue;
			// entity reference
		case '&':
			// try to parse char entity inline
			int nCharRefEndPos, nCharCount;
			EntityType entityType;
			if ((nCharRefEndPos = ParseCharRefInline(nPos, OUT nCharCount, OUT entityType)) > 0)
			{
				if (rcount > 0)
				{
					ShiftBuffer(rpos + rcount, rpos, nPos - rpos - rcount);
				}
				rpos = nPos - rcount;
				rcount += (nCharRefEndPos - nPos - nCharCount);
				nPos = nCharRefEndPos;

				if (!XMLCharType::IsWhiteSpace(pChars[nCharRefEndPos-nCharCount]) || (m_bV1Compat && entityType == EntityTypeCharacterDec))
				{
					orChars |= 0xFF;
				}
			}
			else
			{
				// check error state for ParseCharRefInline.
				if (IsErrorOccurred())
					return false;

				if (nPos > m_ps->m_nCharPos)
				{
					goto ReturnPartialValue;
				}
				switch (HandleEntityReference(false, EntityExpandTypeAll, OUT nPos))
				{
				case EntityTypeUnexpanded:
					// make sure we will report EntityReference after the text node
					m_nextParsingFunction = m_parsingFunction;
					m_parsingFunction = ParsingFunctionEntityReference;
					// end the value (returns nothing)
					goto NoValue;
				case EntityTypeCharacterDec:
					orChars |= 0xFF;
					break;
				case EntityTypeCharacterHex:
				case EntityTypeCharacterNamed:
					if (!XMLCharType::IsWhiteSpace(m_ps->m_pChars[nPos-1]))
					{
						orChars |= 0xFF;
					}
					break;
				default:
					if (IsErrorOccurred())
						return false;
					nPos = m_ps->m_nCharPos;
					break;
				}
				pChars = m_ps->m_pChars;
			}
			continue;
		case ']':
			if (m_ps->m_nCharsUsed - nPos < 3 && !m_ps->m_bIsEof)
			{
				goto ReadData;
			}
			if (pChars[nPos+1] == ']' && pChars[nPos+2] == '>')
			{
				SetLastError(XML_CDATAEndInText);
				return false;
			}
			orChars |= ']';
			nPos++;
			continue;
		default:
			// end of buffer
			if (nPos == m_ps->m_nCharsUsed)
			{
				goto ReadData;
			}
			// surrogate chars
			else
			{
				UCHAR ch = pChars[nPos];
				if (CharUnicodeInfo::IsHighSurrogate(ch))
				{
					if (nPos + 1 == m_ps->m_nCharsUsed)
					{
						goto ReadData;
					}
					nPos++;
					if (CharUnicodeInfo::IsLowSurrogate(pChars[nPos]))
					{
						nPos ++;
						orChars |= ch;
						continue;
					}
				}
				int nOffset = nPos - m_ps->m_nCharPos;

				// TODO: ZeroEndingStream

				SetLastError(XML_InvalidCharacter, (char)ch, m_ps->m_nCharPos + nOffset);
				return false;
				break;
			}
		}

ReadData:
		if (nPos > m_ps->m_nCharPos)
		{
			goto ReturnPartialValue;
		}
		// read new characters into the buffer 
		if (ReadData() == 0)
		{
			if (m_ps->m_nCharsUsed - m_ps->m_nCharPos > 0)
			{
				if (m_ps->m_pChars[m_ps->m_nCharPos] != 0xD)
				{
					BFX_ASSERT1 (false, "We should never get to this point.");
					SetLastError(XML_UnexpectedEOF);
					return false;
				}
				BFX_ASSERT(m_ps->m_bIsEof);
			}
			else
			{
				if (!IsInEntity())
				{
					// end the value (returns nothing)
					goto NoValue;
				}
				if (HandleEntityEnd(true))
				{
					// NOTE: return true means entity resolved manually! the node should be reported.

					// report EndEntity after the text node
					m_nextParsingFunction = m_parsingFunction;
					m_parsingFunction = ParsingFunctionReportEndEntity;
					// end the value (returns nothing)
					goto NoValue;
				}
			}
		}
		nPos = m_ps->m_nCharPos;
		pChars = m_ps->m_pChars;
		continue;
	}
NoValue:
	nStartPos = nEndPos = nPos;
	return true;

ReturnPartialValue:
	if (m_parsingMode == ParsingModeFull && rcount > 0)
	{
		ShiftBuffer(rpos + rcount, rpos, nPos - rpos - rcount);
	}
	nStartPos = m_ps->m_nCharPos;
	nEndPos = nPos - rcount;
	m_ps->m_nCharPos = nPos;
	outOrChars = orChars;
	return c == '<';
}

// When in ParsingStatePartialTextValue, this method parses and caches the rest of the value and stores it in curNode.
bool REFXMLTextReaderImpl::FinishPartialValue()
{
	BFX_ASSERT(m_pStringBuilder->GetLength() == 0);
	BFX_ASSERT(m_parsingFunction == ParsingFunctionPartialTextValue ||
		(m_parsingFunction == ParsingFunctionInReadValueChunk && m_incReadState == IncrementalReadStateReadValueChunk_OnPartialValue));

	m_pCurNode->CopyTo(m_nReadValueOffset, m_pStringBuilder);

	int nStartPos;
	int nEndPos;
	int orChars = 0;
	while (!ParseText(OUT nStartPos, OUT nEndPos, INOUT orChars))
	{
		if (IsErrorOccurred())
			return false;
		m_pStringBuilder->Append(m_ps->m_pChars, nStartPos, nEndPos - nStartPos);
	}
	m_pStringBuilder->Append(m_ps->m_pChars, nStartPos, nEndPos - nStartPos);

	BFX_ASSERT(m_pStringBuilder->GetLength() > 0);
	m_pCurNode->SetValue(m_pStringBuilder->ToString());
	m_pStringBuilder->SetLength(0);
	return true;
}

bool REFXMLTextReaderImpl::FinishOtherValueIterator()
{
	switch (m_parsingFunction)
	{
	case ParsingFunctionInReadAttributeValue:
		// NOTE: do nothing, correct value is already in m_pCurNode
		break;
	case ParsingFunctionInReadValueChunk:
		if (m_incReadState == IncrementalReadStateReadValueChunk_OnPartialValue)
		{
			if (!FinishPartialValue())
				return false;
			m_incReadState = IncrementalReadStateReadValueChunk_OnCachedValue;
		}
		else
		{
			if (m_nReadValueOffset > 0)
			{
				m_pCurNode->SetValue(m_pCurNode->m_strValue.Substring(m_nReadValueOffset));
				m_nReadValueOffset = 0;
			}
		}
		break;
	case ParsingFunctionInReadContentAsBinary:
	case ParsingFunctionInReadElementContentAsBinary:
		switch (m_incReadState)
		{
		case IncrementalReadStateReadContentAsBinary_OnPartialValue:
			FinishPartialValue();
			m_incReadState = IncrementalReadStateReadContentAsBinary_OnCachedValue;
			break;
		case IncrementalReadStateReadContentAsBinary_OnCachedValue:
			if (m_nReadValueOffset > 0)
			{
				m_pCurNode->SetValue(m_pCurNode->m_strValue.Substring(m_nReadValueOffset));
				m_nReadValueOffset = 0;
			}
			break;
		case IncrementalReadStateReadContentAsBinary_End:
			m_pCurNode->m_strValue.SetEmpty();
			break;
		default:
			break;
		}
		break;
	default:
		break;
	}
	return true;
}

// Reads the attributes
bool REFXMLTextReaderImpl::ParseAttributes()
{
	int nPos = m_ps->m_nCharPos;
	UCHAR* pChars = m_ps->m_pChars;
	NodeData* pAttr = NULL;

	BFX_ASSERT(m_nAttrCount == 0);

	for (;;)
	{
		int nLineNoDelta = 0;
		UCHAR tmpch0;
		UCHAR tmpch1;

		bool isNCStartName;
		UCHAR quoteChar;
		int nAttrNameLinePos;
		int nAttrNameLineNo;
		int nColonPos;

		// eat whitespaces
		while (XMLCharType::IsWhiteSpace(tmpch0 = pChars[nPos]))
		{
			if (tmpch0 == (UCHAR)0xA)
			{
				OnNewLine(nPos + 1);
				nLineNoDelta ++;
			}
			else if (tmpch0 == (UCHAR)0xD)
			{
				if (pChars[nPos+1] == (UCHAR)0xA)
				{
					OnNewLine(nPos + 2);
					nLineNoDelta ++;
					nPos ++;
				}
				else if (nPos+1 != m_ps->m_nCharsUsed)
				{
					OnNewLine(nPos + 1);
					nLineNoDelta ++;
				}
				else
				{
					m_ps->m_nCharPos = nPos;
					goto ReadData;
				}
			}
			nPos ++;
		}

		isNCStartName = XMLCharType::IsStartNameChar(tmpch1 = pChars[nPos]);

		if (!isNCStartName)
		{
			// element end
			if (tmpch1 == '>')
			{
				BFX_ASSERT(m_pCurNode->m_type == XMLNodeType_Element);
				m_ps->m_nCharPos = nPos + 1;
				m_parsingFunction = ParsingFunctionMoveToElementContent;
				goto End;
			}
			// empty element end
			else if (tmpch1 == '/')
			{
				BFX_ASSERT(m_pCurNode->m_type == XMLNodeType_Element);
				if (nPos + 1 == m_ps->m_nCharsUsed)
				{
					goto ReadData;
				}
				if (pChars[nPos+1] == '>')
				{
					m_ps->m_nCharPos = nPos + 2;
					m_pCurNode->SetEmptyElement(true);
					m_nextParsingFunction = m_parsingFunction;
					m_parsingFunction = ParsingFunctionPopEmptyElementContext;
					goto End;
				}
				else
				{
					SetLastError(XML_UnexpectedToken, CSTR(">"), nPos + 1);
					return false;
				}
			}
			else if (nPos == m_ps->m_nCharsUsed)
			{
				goto ReadData;
			}
			else if (tmpch1 != ':' || m_bSupportNamespaces)
			{
				SetLastError(XML_BadStartNameChar, (char)tmpch1, nPos);
				return false;
			}
		}

		if (nPos == m_ps->m_nCharPos)
		{
			SetLastError(XML_ExpectingWhiteSpace, CSTR(ParseUnexpectedToken()));
			return false;
		}
		m_ps->m_nCharPos = nPos;

		// save attribute name line position
		nAttrNameLinePos = m_ps->GetLinePos();
#ifdef _DEBUG
		nAttrNameLineNo = m_ps->m_nLineNo;
#endif

		// parse attribute name
		nColonPos = -1;

		// PERF: we intentionally don't call ParseQName here to parse the element name unless a special 
		// case occurs (like end of buffer, invalid name char)
		nPos++;	// start name char has already been checked

		// parse attribute name
ContinueParseName:
		UCHAR tmpch2;
		while (XMLCharType::IsNCNameChar(tmpch2 = pChars[nPos]))
		{
			nPos++;
		}

		// colon -> save prefix end position and check next char if it's name start char
		if (tmpch2 == ':')
		{
			if (nColonPos != -1)
			{
				if (m_bSupportNamespaces)
				{
					SetLastError(XML_BadNameChar, ":");
					return false;
				}
				else
				{
					nPos ++;
					goto ContinueParseName;
				}
			}
			else
			{
				nColonPos = nPos;
				nPos ++;
				if (XMLCharType::IsStartNCNameChar(pChars[nPos]))
				{
					nPos ++;
					goto ContinueParseName;
				}
				nPos = ParseQName(OUT nColonPos);
				if (nPos == -1)	// returns -1 means error occurs.
					return false;

				pChars = m_ps->m_pChars;
			}
		}
		else if (nPos == m_ps->m_nCharsUsed)
		{
			nPos = ParseQName(OUT nColonPos);
			if (nPos == -1)	// returns -1 means error occurs.
				return false;
			pChars = m_ps->m_pChars;
		}

		pAttr = AddAttribute(nPos, nColonPos);
		pAttr->SetLineInfo(m_ps->m_nLineNo, nAttrNameLinePos);

#ifdef _DEBUG
		BFX_ASSERT(nAttrNameLineNo == m_ps->m_nLineNo);
#endif

		// parse equals and quote char; 
		if (pChars[nPos] != '=')
		{
			m_ps->m_nCharPos = nPos;
			EatWhitespaces(NULL);
			nPos = m_ps->m_nCharPos;
			if (pChars[nPos] != '=')
			{
				SetLastError(XML_UnexpectedToken, "=");
				return false;
			}
		}
		nPos ++;

		quoteChar = pChars[nPos];
		if (quoteChar != '"' && quoteChar != '\'')
		{
			m_ps->m_nCharPos = nPos;
			EatWhitespaces(NULL);
			nPos = m_ps->m_nCharPos;
			quoteChar = pChars[nPos];
			if (quoteChar != '"' && quoteChar != '\'')
			{
				SetLastError(XML_UnexpectedToken, "\"");
				return false;
			}
		}
		nPos ++;
		m_ps->m_nCharPos = nPos;

		pAttr->m_quoteChar = quoteChar;
		pAttr->SetLineInfo2(m_ps->m_nLineNo, m_ps->GetLinePos());

		// parse attribute value
		UCHAR tmpch3;
		while (XMLCharType::IsAttributeValueChar(tmpch3 = pChars[nPos]))
		{
			nPos++;
		}

		if (tmpch3 == quoteChar)
		{
			UniString strVal(pChars + m_ps->m_nCharPos, nPos - m_ps->m_nCharPos);
#ifdef _DEBUG
			if (m_bNormalize)
			{
				// NOTE: Check the attribute value is CDATA normalized!
			}
#endif
			pAttr->SetValue(strVal);
			nPos++;
			m_ps->m_nCharPos = nPos;
		}
		else
		{
			ParseAttributeValueSlow(nPos, quoteChar, pAttr);
			nPos = m_ps->m_nCharPos;
			pChars = m_ps->m_pChars;
		}

		// handle special attributes:
		if (pAttr->m_strPrefix.IsEmpty())
		{
			// default namespace declaration
			if (pAttr->m_strLocalName == m_strXmlNs)
			{
				OnDefaultNamespaceDecl(pAttr);
			}
		}
		else
		{
			// prefixed namespace declaration
			if (pAttr->m_strPrefix == m_strXmlNs)
			{
				OnNamespaceDecl(pAttr);
			}
			// xml: attribute
			else if (pAttr->m_strPrefix == m_strXml)
			{
				OnXmlReservedAttribute(pAttr);
			}
		}
		continue;

ReadData:
		m_ps->m_nLineNo -= nLineNoDelta;
		if (ReadData() != 0)
		{
			nPos = m_ps->m_nCharPos;
			pChars = m_ps->m_pChars;
		}
		else
		{
			SetLastError(XML_UnexpectedEOFInElementContent);
			return false;
		}
	}

End:
	// TODO: add default attributes and normalize if necessary.

	// lookup namespaces: element
	ElementNamespaceLookup();

	// lookup namespaces: attributes
	if (m_bAttrNeedNamespaceLookup)
	{
		AttributeNamespaceLookup();
		m_bAttrNeedNamespaceLookup = false;
	}

	// TODO: check duplicate attributes
#if	0
	if (attrDuplWalkCount >= MaxAttrDuplWalkCount )
	{
		AttributeDuplCheck();
	}
#endif
	return true;
}

// Parses DOCTYPE declaration
bool REFXMLTextReaderImpl::ParseDoctypeDecl()
{
	// TODO: prohibitDtd
	// parse 'DOCTYPE'
	while (m_ps->m_nCharsUsed - m_ps->m_nCharPos < 8)
	{
		if (ReadData() == 0)
		{
			SetLastError(XML_UnexpectedEOF, "DOCTYPE");
			return false;
		}
	}
	if (UniString(m_ps->m_pChars + m_ps->m_nCharPos, 7) != USTR("DOCTYPE"))
	{
		SetLastError(XML_UnexpectedToken, !m_bRootElementParsed ? "DOCTYPE" : "<!--");
		return false;
	}
	if (!XMLCharType::IsWhiteSpace(m_ps->m_pChars[m_ps->m_nCharPos + 7]))
	{
		SetLastError(XML_ExpectingWhiteSpace, CSTR(ParseUnexpectedToken(m_ps->m_nCharPos + 7)));
		return false;
	}

	if (m_bRootElementParsed)
	{
		SetLastError(XML_DtdAfterRootElement/*, m_ps->m_nCharPos - 2*/);
		return false;
	}

	m_ps->m_nCharPos += 8;

	EatWhitespaces(NULL);
	m_pCurNode->SetLineInfo(m_ps->m_nLineNo, m_ps->GetLinePos());

	// TODO: parse DTD
	m_pDtdParserProxy = new DtdParserProxy(this);
	m_pDtdParserProxy->Parse(true);

	REFXMLSchemaInfo* pDtdSchemaInfo = m_pDtdParserProxy->GetDtdSchemaInfo();

	// TODO: add default attributes and normalize if necessary.

	m_pCurNode->SetNamedNode(XMLNodeType_DocumentType, pDtdSchemaInfo->GetDocTypeName()->ToString());
	m_pCurNode->SetValue(m_pDtdParserProxy->GetInternalDtdSubset());

	m_nextParsingFunction = m_parsingFunction;
	m_parsingFunction = ParsingFunctionResetAttributesRootLevel;

	return true;
}

// Parses PI value. returns true if success, false if only rartial value, in the case we should
// call GetLastError() to check the error state.
bool REFXMLTextReaderImpl::ParsePIValue(OUT int& nOutStartPos, OUT int& nOutEndPos)
{
	// read new characters into the buffer
	if (m_ps->m_nCharsUsed - m_ps->m_nCharPos < 2)
	{
		if (ReadData() == 0)
		{
			SetLastError(XML_UnexpectedEOF, CSTR("PI")/*, m_ps->m_nCharsUsed*/);
			return false;
		}
	}

	int nPos = m_ps->m_nCharPos;
	UCHAR* pChars = m_ps->m_pChars;
	int rcount = 0;
	int rpos = -1;
	for (;;)
	{
		while (XMLCharType::IsTextChar(pChars[nPos]) && pChars[nPos] != '?')
		{ 
				nPos++;
		}

		switch (pChars[nPos])
		{
			// possibly end of PI
		case '?':
			if (pChars[nPos+1] == '>')
			{
				if (rcount > 0)
				{
					ShiftBuffer(rpos + rcount, rpos, nPos - rpos - rcount);
					nOutEndPos = nPos - rcount;
				}
				else
				{
					nOutEndPos = nPos;
				}
				nOutStartPos = m_ps->m_nCharPos;
				m_ps->m_nCharPos = nPos + 2;
				return true;
			}
			else if (nPos+1 == m_ps->m_nCharsUsed)
			{
				goto ReturnPartial;
			}
			else
			{
				nPos++;
				continue;
			}
			// eol
		case 0xA:
			nPos++;
			OnNewLine(nPos);
			continue;
		case 0xD:
			if (pChars[nPos+1] == 0xA)
			{
				if (m_parsingMode == ParsingModeFull)
				{
					// EOL normalization of 0xD 0xA
					if (nPos - m_ps->m_nCharPos > 0)
					{
						if (rcount == 0)
						{
							rcount = 1;
							rpos = nPos;
						}
						else
						{
							ShiftBuffer(rpos + rcount, rpos, nPos - rpos - rcount);
							rpos = nPos - rcount;
							rcount ++;
						}
					}
					else
					{
						m_ps->m_nCharPos++;
					}
				}
				nPos += 2;
			}
			else if (nPos+1 < m_ps->m_nCharsUsed || m_ps->m_bIsEof)
			{
				pChars[nPos] = 0xA;
				nPos++;
			}
			else
			{
				goto ReturnPartial;
			}
			OnNewLine(nPos);
			continue;
		case '<':
		case '&':
		case ']':
		case 0x9:
			nPos++;
			continue;
		default:
			// end of buffer
			if (nPos == m_ps->m_nCharsUsed)
			{
				goto ReturnPartial;
			}
			// surrogate characters
			else
			{
				UCHAR ch = pChars[nPos];
				if (CharUnicodeInfo::IsHighSurrogate(ch))
				{
					if (nPos + 1 == m_ps->m_nCharsUsed)
					{
						goto ReturnPartial;
					}
					nPos++;
					if (CharUnicodeInfo::IsLowSurrogate(pChars[nPos]))
					{
						nPos++;
						continue;
					}
				}
				SetLastError(XML_InvalidCharacter/*, ch*/);
				return false;
				break;
			}
		}

	}

ReturnPartial:
	if (rcount > 0)
	{
		ShiftBuffer(rpos + rcount, rpos, nPos - rpos - rcount);
		nOutEndPos = nPos - rcount;
	}
	else
	{
		nOutEndPos = nPos;
	}
	nOutStartPos = m_ps->m_nCharPos;
	m_ps->m_nCharPos = nPos;
	return false;
}

bool REFXMLTextReaderImpl::ParseAttributeValueSlow(int nCurPos, UCHAR quoteChar, NodeData* pAttr)
{
	int nPos = nCurPos;
	UCHAR* pChars = m_ps->m_pChars;
	int nAttributeBaseEntityId = m_ps->m_nEntityId;
	int nValueChunkStartPos = 0;
	XMLLineInfo valueChunkLineInfo(m_ps->m_nLineNo, m_ps->GetLinePos());
	REF<NodeData> pLastChunk;

	BFX_ASSERT(m_pStringBuilder->GetLength() == 0);

	for (;;)
	{
		// parse the rest of the attribute value
		while (XMLCharType::IsAttributeValueChar(pChars[nPos]))
		{
			nPos ++;
		}

		if (nPos - m_ps->m_nCharPos > 0)
		{
			m_pStringBuilder->Append(pChars, m_ps->m_nCharPos, nPos - m_ps->m_nCharPos);
			m_ps->m_nCharPos = nPos;
		}

		if (pChars[nPos] == quoteChar && nAttributeBaseEntityId == m_ps->m_nEntityId)
		{
			break;
		}
		else
		{
			int nEnclosingEntityId;
			XMLLineInfo entityLineInfo;

			switch (pChars[nPos])
			{
				// eol
			case 0xA:
				nPos ++;
				OnNewLine(nPos);
				if (m_bNormalize)
				{
					m_pStringBuilder->Append((UCHAR)0x20);	// CDATA normalization of 0xA
					m_ps->m_nCharPos++;
				}
				continue;
			case 0xD:
				if (pChars[nPos+1] == 0xA)
				{
					nPos += 2;
					if (m_bNormalize)
					{
						m_pStringBuilder->Append(USTR("\20"));	// CDATA normalization of 0xD 0xA
						m_ps->m_nCharPos = nPos;
					}
				}
				else if (nPos + 1 < m_ps->m_nCharsUsed || m_ps->m_bIsEof)
				{
					nPos ++;
					if (m_bNormalize)
					{
						m_pStringBuilder->Append((UCHAR)0x20);	// CDATA normalization of 0xD and 0xD 0xA
						m_ps->m_nCharPos = nPos;
					}
				} 
				else
				{
					goto ReadData;
				}
				OnNewLine(nPos);
				continue;
				// tab
			case 0x9:
				nPos ++;
				if (m_bNormalize)
				{
					m_pStringBuilder->Append((UCHAR)0x20);	// CDATA normalization of 0x9
					m_ps->m_nCharPos++;
				}
				continue;
			case '"':
			case '\'':
			case '>':
				nPos++;
				continue;
				// attribute values cannot contain '<'
				// NOTE: Is it necessary???
			case '<':
				SetLastError(XML_BadAttributeChar/*, '<', nPos*/);
				break;
				// entity referece
			case '&':
				if (nPos - m_ps->m_nCharPos > 0)
				{
					m_pStringBuilder->Append(pChars, m_ps->m_nCharPos, nPos - m_ps->m_nCharPos);
				}
				m_ps->m_nCharPos = nPos;

				nEnclosingEntityId = m_ps->m_nEntityId;
				entityLineInfo.Set(m_ps->m_nLineNo, m_ps->GetLinePos() + 1);
				switch (HandleEntityReference(true, EntityExpandTypeAll, OUT nPos))
				{
				case EntityTypeCharacterDec:
				case EntityTypeCharacterHex:
				case EntityTypeCharacterNamed:
					break;
				case EntityTypeUnexpanded:
					if (m_parsingMode == ParsingModeFull && m_ps->m_nEntityId == nAttributeBaseEntityId)
					{
						// construct text value chunk
						int nValueChunkLen = m_pStringBuilder->GetLength() - nValueChunkStartPos;
						if (nValueChunkLen > 0)
						{
							REF<NodeData> pTextChunk = new NodeData();
							pTextChunk->m_lineInfo = valueChunkLineInfo;
							pTextChunk->m_nDepth = pAttr->m_nDepth + 1;
							pTextChunk->SetValueNode(XMLNodeType_Text, m_pStringBuilder->ToString().Substring(nValueChunkStartPos, nValueChunkLen));
							AddAttributeChunkToList(pAttr, pTextChunk, INOUT pLastChunk);
						}

						// parse entity name
						m_ps->m_nCharPos++;
						UniString strEntityName = ParseEntityName();

						// construct entity reference chunk
						REF<NodeData> pEntityChunk = new NodeData();
						pEntityChunk->m_lineInfo = entityLineInfo;
						pEntityChunk->m_nDepth = pAttr->m_nDepth + 1;
						pEntityChunk->SetNamedNode(XMLNodeType_EntityReference, strEntityName);
						AddAttributeChunkToList(pAttr, pEntityChunk, INOUT pLastChunk);

						// append entity ref to the attribute value
						m_pStringBuilder->Append('&');
						m_pStringBuilder->Append(strEntityName);
						m_pStringBuilder->Append(';');

						// update info for the next attribute value chunk
						nValueChunkStartPos = m_pStringBuilder->GetLength();
						valueChunkLineInfo.Set(m_ps->m_nLineNo, m_ps->GetLinePos());

						m_bFullAttrCleanup = true;
					}
					else
					{
						m_ps->m_nCharPos++;
						ParseEntityName();
					}

					nPos = m_ps->m_nCharPos;
					break;
				case EntityTypeExpandedInAttribute:
					if (m_parsingMode == ParsingModeFull && nEnclosingEntityId == nAttributeBaseEntityId)
					{
						// construct text value chunk
						int nValueChunkLen = m_pStringBuilder->GetLength() - nValueChunkStartPos;
						if (nValueChunkLen > 0)
						{
							REF<NodeData> pTextChunk = new NodeData();
							pTextChunk->m_lineInfo = valueChunkLineInfo;
							pTextChunk->m_nDepth = pAttr->m_nDepth + 1;
							pTextChunk->SetValueNode(XMLNodeType_Text, m_pStringBuilder->ToString().Substring(nValueChunkStartPos, nValueChunkLen));
							AddAttributeChunkToList(pAttr, pTextChunk, INOUT pLastChunk);
						}

						// construct entity reference chunk
						REF<NodeData> pEntityChunk = new NodeData();
						pEntityChunk->m_lineInfo = entityLineInfo;
						pEntityChunk->m_nDepth = pAttr->m_nDepth + 1;
						pEntityChunk->SetNamedNode(XMLNodeType_EntityReference, m_ps->m_pEntity->GetName()->GetName());
						AddAttributeChunkToList(pAttr, pEntityChunk, INOUT pLastChunk);

						m_bFullAttrCleanup = true;

						// NOTE: info for the next attribute value chunk will be updated once we
						// get out of the expanded entity
					}

					nPos = m_ps->m_nCharPos;
					break;
				default:
					if (IsErrorOccurred())
						return false;
					nPos = m_ps->m_nCharPos;
					break;
				}

				pChars = m_ps->m_pChars;
				continue;
			default:
				// end of buffer
				if (nPos == m_ps->m_nCharsUsed)
				{
					goto ReadData;
				}
				// surrogate chars
				else
				{
					UCHAR ch = pChars[nPos];
					if (CharUnicodeInfo::IsHighSurrogate(ch))
					{
						if (nPos + 1 == m_ps->m_nCharsUsed)
						{
							goto ReadData;
						}
						nPos ++;
						if (CharUnicodeInfo::IsHighSurrogate(pChars[nPos]))
						{
							nPos++;
							continue;
						}
					}
					SetLastError(XML_InvalidCharacter, CSTR((char)ch));
					return false;
				}
			}
		}

ReadData:
		// read new characters into the buffer
		if (ReadData() == 0)
		{
			if (m_ps->m_nCharsUsed - m_ps->m_nCharPos > 0 )
			{
				// NOTE: Is it necessary???
				if (m_ps->m_pChars[m_ps->m_nCharPos] != 0x0D)
				{
					BFX_ASSERT1(false, "We should never get to this point.");
					SetLastError(XML_UnexpectedEOF);
					return false;
				}
				BFX_ASSERT(m_ps->m_bIsEof);
			}
			else
			{
				if (!IsInEntity())
				{
					if (m_fragmentType == XMLNodeType_Attribute)
					{
						if (nAttributeBaseEntityId != m_ps->m_nEntityId)
						{
							SetLastError(XML_EntityRefNesting);
							return false;
						}
						break;
					}
					SetLastError(XML_UnclosedQuote);
					return false;
				}

				if (HandleEntityEnd(true))
				{
					// no EndEntity reporting while parsing attributes
					BFX_ASSERT(false);
					SetLastError(XML_InternalError);
					return false;
				}
				// update info for the next attribute value chunk
				if (nAttributeBaseEntityId == m_ps->m_nEntityId)
				{
					nValueChunkStartPos = m_pStringBuilder->GetLength();
					valueChunkLineInfo.Set(m_ps->m_nLineNo, m_ps->GetLinePos());
				}
			}
		}

		nPos = m_ps->m_nCharPos;
		pChars = m_ps->m_pChars;
	}
	BFX_ASSERT(!IsErrorOccurred());

	if (pAttr->m_pNextAttrValueChunk != NULL)
	{
		// construct last text value chunk
		int nValueChunkLen = m_pStringBuilder->GetLength() - nValueChunkStartPos;
		if (nValueChunkLen > 0)
		{
			REF<NodeData> pTextChunk = new NodeData();
			pTextChunk->m_lineInfo = valueChunkLineInfo;
			pTextChunk->m_nDepth = pAttr->m_nDepth + 1;
			pTextChunk->SetValueNode(XMLNodeType_Text, m_pStringBuilder->ToString().Substring(nValueChunkStartPos, nValueChunkLen));
			AddAttributeChunkToList(pAttr, pTextChunk, INOUT pLastChunk);
		}
	}

	m_ps->m_nCharPos = nPos + 1;

	pAttr->SetValue(m_pStringBuilder->ToString());
	m_pStringBuilder->SetLength(0);

	return true;
}

void REFXMLTextReaderImpl::AddAttributeChunkToList(NodeData* pAttr, NodeData* pChunk, INOUT NodeData* pLastChunk)
{
	if (pLastChunk == NULL)
	{
		BFX_ASSERT(pAttr->m_pNextAttrValueChunk == NULL);
		pLastChunk = pChunk;
		pAttr->m_pNextAttrValueChunk = pChunk;
	}
	else
	{
		pLastChunk->m_pNextAttrValueChunk = pChunk;
		pLastChunk = pChunk;
	}
}

bool REFXMLTextReaderImpl::ParseAttributeValueChunk()
{
	UCHAR*	pChars = m_ps->m_pChars;
	int nPos  = m_ps->m_nCharPos;

	m_pCurNode = AddNode(m_nCurNodeIndex + m_nAttrCount + 1, m_nCurNodeIndex + 2);
	m_pCurNode->SetLineInfo(m_ps->m_nLineNo, m_ps->GetLinePos());

	if (m_bEmptyEntityInAttributeResolved)
	{
		m_pCurNode->SetValueNode(XMLNodeType_Text, UniString());
		m_bEmptyEntityInAttributeResolved = true;
		return true;
	}

	BFX_ASSERT(m_pStringBuilder->GetLength() == 0);

	for (;;)
	{
		while (XMLCharType::IsAttributeValueChar(pChars[nPos]))
			nPos ++;

		switch (pChars[nPos])
		{
			// eol D
		case 0xD:
			nPos ++;
			continue;
			// eol A, tab
		case 0xA:
		case 0x9:
			if (m_bNormalize)
			{
				pChars[nPos] = 0x20;	// CDATA normalization of 0xA and 0x9
			}
			nPos++;
			continue;
		case '"':
		case '\'':
		case '>':
			nPos++;
			continue;
			// attribute values cannot contain '<'
		case '<':
			SetLastError(XML_BadAttributeChar, '<');
			return false;
		case '&':
			// entity reference
			if (nPos - m_ps->m_nCharPos > 0)
			{
				m_pStringBuilder->Append(pChars, m_ps->m_nCharPos, nPos - m_ps->m_nCharPos);
			}
			m_ps->m_nCharPos = nPos;

			// expand char entities but not general entities 
			switch (HandleEntityReference(true, EntityExpandTypeOnlyCharacter, OUT nPos))
			{
			case EntityTypeCharacterDec:
			case EntityTypeCharacterHex:
			case EntityTypeCharacterNamed:
				pChars = m_ps->m_pChars;
				if (m_bNormalize && XMLCharType::IsWhiteSpace(pChars[m_ps->m_nCharPos]) && nPos - m_ps->m_nCharPos == 1)
				{
					pChars[m_ps->m_nCharPos] = 0x20;	// CDATA normalization of character references in entities
				}
				break;
			case EntityTypeUnexpanded:
				if (m_pStringBuilder->GetLength() == 0)
				{
					m_pCurNode->m_lineInfo.m_nLinePos++;
					m_ps->m_nCharPos++;
					UniString strEntityName = ParseEntityName();
					if (strEntityName.IsEmpty())	// NOTE: we can't pass a empty localName to m_pCurNode->SetNamedNode.
						return false;
					m_pCurNode->SetNamedNode(XMLNodeType_EntityReference, strEntityName);
					return true;
				}
				else
				{
					goto ReturnText;
				}
			case EntityTypeError:
				return false;
			default:
				BFX_ASSERT1(false, "We should never get to this point.");
				break;
			}
			pChars = m_ps->m_pChars;
			continue;
		default:
			// end of buffer
			if (nPos == m_ps->m_nCharsUsed)
			{
				goto ReadData;
			}
			// surrogate chars
			else
			{
				UCHAR ch = pChars[nPos];
				if (CharUnicodeInfo::IsHighSurrogate(ch))
				{
					if (nPos + 1 == m_ps->m_nCharsUsed)
					{
						goto ReadData;
					}
					nPos++;
					if (CharUnicodeInfo::IsLowSurrogate(pChars[nPos]))
					{
						nPos++;
						continue;
					}
				}
				SetLastError(XML_InvalidCharacter, nPos, (char)ch);
				return false;
			}
		}
ReadData:
		if (nPos - m_ps->m_nCharPos > 0)
		{
			m_pStringBuilder->Append(pChars, m_ps->m_nCharPos, nPos - m_ps->m_nCharPos);
			m_ps->m_nCharPos = nPos;
		}
		// read new characters into the buffer
		if (ReadData() == 0)
		{
			if (m_pStringBuilder->GetLength() > 0)
			{
				goto ReturnText;
			}
			else {
				if (HandleEntityEnd(false))
				{
					// TODO: SetupEndEntityNodeInAttribute();
					BFX_ASSERT(false);
					return true;
				}
				else
				{
					BFX_ASSERT1(false, "We should never get to this point.");
				}
			}
		}

		nPos = m_ps->m_nCharPos;
		pChars = m_ps->m_pChars;
	}
ReturnText:
	if (nPos - m_ps->m_nCharPos > 0)
	{
		m_pStringBuilder->Append(pChars, m_ps->m_nCharPos, nPos - m_ps->m_nCharPos);
		m_ps->m_nCharPos = nPos;
	}
	m_pCurNode->SetValueNode(XMLNodeType_Text, m_pStringBuilder->ToString());
	m_pStringBuilder->SetLength(0);
	return true;
}

// Parse the name or qname. returns the end position, or -1 if error occurs.
int REFXMLTextReaderImpl::ParseName()
{
	int nColonPos;
	return ParseName(false, 0, nColonPos);
}

int REFXMLTextReaderImpl::ParseQName(OUT int& nColonPos)
{
	return ParseName(true, 0, nColonPos);
}

int REFXMLTextReaderImpl::ParseName(bool bIsQName, int iStartOffset, OUT int& nColonPos)
{
	int nColonOffset = -1;
	int nPos = m_ps->m_nCharPos + iStartOffset;

ContinueStartName:
	UCHAR* pChars = m_ps->m_pChars;
	// start name char
	if (!XMLCharType::IsStartNCNameChar(pChars[nPos]))
	{
		if (nPos == m_ps->m_nCharsUsed)
		{
			if ((nPos = ReadDataInName(nPos)) != -1)
			{
				goto ContinueStartName;
			}
			SetLastError(XML_UnexpectedEOF, "Name");
			return -1;
		}
		// TODO; support namespaces????
	}
	nPos++;

ContinueName:
	// parse name
	while (XMLCharType::IsNCNameChar(pChars[nPos]))
	{
		nPos++;
	}

	// colon
	if (pChars[nPos] == ':')
	{
		// TODO: support namespaces????

		nColonOffset = nPos - m_ps->m_nCharPos;
		nPos ++;
		goto ContinueStartName;
	}
	// end of buffer
	else if (nPos == m_ps->m_nCharsUsed)
	{
		if ((nPos = ReadDataInName(nPos)) != -1)
		{
			pChars = m_ps->m_pChars;
			goto ContinueName;
		}
		SetLastError(XML_UnexpectedEOF, "Name");
		return -1;
	}

	// end of name
	nColonPos = (nColonOffset == -1) ? -1 : m_ps->m_nCharPos + nColonOffset;
	return nPos;
}

bool REFXMLTextReaderImpl::ParseEntityReference()
{
	BFX_ASSERT(m_ps->m_pChars[m_ps->m_nCharPos] == '&');
	m_ps->m_nCharPos++;

	m_pCurNode->SetLineInfo(m_ps->m_nLineNo, m_ps->GetLinePos());
	UniString strEntityName = ParseEntityName();
	if (strEntityName.IsEmpty())
		return false;
	m_pCurNode->SetNamedNode(XMLNodeType_EntityReference, strEntityName);
	return true;
}

UniString REFXMLTextReaderImpl::ParseEntityName()
{
	int nEndPos = ParseName();
	if (nEndPos == -1)
	{
		SetLastError(XML_ErrorParsingEntityName);
		return UniString();
	}

	// check ';'
	if (m_ps->m_pChars[nEndPos] != ';')
	{
		SetLastError(XML_ErrorParsingEntityName);
		return UniString();
	}

	UniString strEntityName = m_pNameTable->Add(m_ps->m_pChars, m_ps->m_nCharPos, nEndPos - m_ps->m_nCharPos);
	m_ps->m_nCharPos = nEndPos + 1;
	return strEntityName;
}

// Parses numeric character entity reference (e.g. &#32; &#x20;).
//      - replaces the last one or two character of the entity reference (';' and the character before) with the referenced 
//        character or surrogates pair (if expand == true)
//      - returns position of the end of the character reference, that is of the character next to the original ';'
//      - if (expand == true) then ps.charPos is changed to point to the replaced character
int REFXMLTextReaderImpl::ParseNumericCharRef(bool bExpand, REFUniStringBuilder* pInternalSubsetBuilder, OUT EntityType& entityType)
{
	for (;;)
	{
		int nNewPos;
		int nCharCount;
		switch (nNewPos = ParseNumericCharRefInline(m_ps->m_nCharPos, bExpand, pInternalSubsetBuilder, OUT nCharCount, OUT entityType))
		{
		case -2:
			// read new characters in the buffer
			if (ReadData() == 0)
			{
				SetLastError(XML_UnexpectedEOF);
				return -1;
			}
			BFX_ASSERT(m_ps->m_pChars[m_ps->m_nCharPos] == '&');
			continue;
		default:
			if (bExpand)
			{
				m_ps->m_nCharPos = nNewPos - nCharCount;
			}
			return nNewPos;
		}
	}
	BFX_ASSERT1(false, "should never run at this point.");
	return -1;
}

// Parses named character entity reference (&amp; &apos; &lt; &gt; &quot;).
// Returns -1 if the reference is not a character entity reference.
// Otherwise 
//      - replaces the last character of the entity reference (';') with the referenced character (if expand == true)
//      - returns position of the end of the character reference, that is of the character next to the original ';'
//      - if (expand == true) then ps.charPos is changed to point to the replaced character
int REFXMLTextReaderImpl::ParseNamedCharRef(bool bExpand, REFUniStringBuilder* pInternalSubsetBuilder)
{
	for (;;)
	{
		int nNewPos;
		switch (nNewPos = ParseNamedCharRefInline(m_ps->m_nCharPos, bExpand, pInternalSubsetBuilder))
		{
		case -1:
			return -1;
		case -2:
			// read new characters in the buffer
			if (ReadData() == 0)
			{
				return -1;
			}
			BFX_ASSERT(m_ps->m_pChars[m_ps->m_nCharPos] == '&');
			continue;
		default:
			if (bExpand)
			{
				m_ps->m_nCharPos = nNewPos - 1;
			}
			return nNewPos;
		}
	}
	BFX_ASSERT1(false, "should never run at this point.");
	return -1;
}

// Parses character entity reference (e.g. &#32; &#x20;&amp; &apos;).
int REFXMLTextReaderImpl::ParseCharRefInline(int nStartPos, OUT int& nCharCount, OUT EntityType& entityType)
{
    BFX_ASSERT(m_ps->m_pChars[nStartPos] == '&');
    if (m_ps->m_pChars[nStartPos + 1] == '#')
	{
        return ParseNumericCharRefInline(nStartPos, true, NULL, OUT nCharCount, OUT entityType);
    }
    else
	{
        nCharCount = 1;
        entityType = EntityTypeCharacterNamed;
        return ParseNamedCharRefInline(nStartPos, true, NULL);
    }
}

// Parses numeric character entity reference (e.g. &#32; &#x20;).
// Returns -2 if more data is needed in the buffer
// Returns -1 means error occurs.
// Otherwise 
//      - replaces the last one or two character of the entity reference (';' and the character before) with the referenced 
//        character or surrogates pair (if expand == true)
//      - returns position of the end of the character reference, that is of the character next to the original ';'
int REFXMLTextReaderImpl::ParseNumericCharRefInline(int nStartPos, bool bExpand, REFUniStringBuilder* pInternalSubsetBuilder, OUT int& nCharCount, OUT EntityType& entityType)
{
	BFX_ASSERT(m_ps->m_pChars[nStartPos] == '&' && m_ps->m_pChars[nStartPos + 1] == '#');

	int val;
	int nPos;
	UCHAR* pChars;

	val = 0;
	XMLError errorState;
	pChars = m_ps->m_pChars;
	nPos = nStartPos + 2;
	nCharCount = 0;

	if (pChars[nPos] == 'x')
	{
		nPos++;
		errorState = XML_BadHexEntity;
		for (;;)
		{
			UCHAR ch = pChars[nPos];
			if (ch >= '0' && ch <= '9')
				val = val * 16 + ch - '0';
			else if (ch >= 'a' && ch <= 'f')
				val = val * 16 + 10 + ch - 'a';
			else if (ch >= 'A' && ch <= 'F')
				val = val * 16 + 10 + ch - 'A';
			else 
				break;
			nPos++;
		}
		entityType = EntityTypeCharacterHex;
	}
	else if (nPos < m_ps->m_nCharsUsed)
	{
		errorState = XML_BadDecimalEntity;
		while (pChars[nPos] >= '0' && pChars[nPos] <= '9')
		{
			val = val * 10 + pChars[nPos] - '0';
			nPos++;
		}
		entityType = EntityTypeCharacterDec;
	}
	else
	{
		// need more data in the buffer
		entityType = EntityTypeUnexpanded;
		return -2;
	}

	if (pChars[nPos] != ';')
	{
		if (nPos == m_ps->m_nCharsUsed)
		{
			// need more data in the buffer
			return -2;
		}
		else
		{
			SetLastError(errorState/*, nPos*/);
			return -1;
		}
	}

	// simple character
	if (val <= 0xffff /*MAX_UCHAR*/)
	{
		UCHAR ch = (UCHAR)val;
		if ((!XMLCharType::IsCharData(ch) || ( ch >= CharUnicodeInfo::W_LOW_SURROGATE_START && ch <= 0xdeff)) && m_bNormalize)
		{
			SetLastError(XML_InvalidCharacter, (char)ch, (m_ps->m_pChars[m_ps->m_nCharPos + 2] == 'x') ? m_ps->m_nCharPos + 3 : m_ps->m_nCharPos + 2);
			return -1;
		}

		if (bExpand)
		{
			if (pInternalSubsetBuilder != NULL)
			{
				pInternalSubsetBuilder->Append(m_ps->m_pChars, m_ps->m_nCharPos, nPos - m_ps->m_nCharPos + 1);
			}
			pChars[nPos] = ch;
		}
		nCharCount = 1;
		return nPos + 1;
	}
	// surrogate
	else
	{
		int v = val - 0x10000;
		int low = CharUnicodeInfo::W_LOW_SURROGATE_START + v % 1024;
		int high = CharUnicodeInfo::W_HIGH_SURROGATE_START + v / 1024;

		if (m_bNormalize)
		{
			UCHAR ch = (UCHAR)high;
			if (CharUnicodeInfo::IsHighSurrogate(ch))
			{
				ch = (UCHAR)low;
				if (CharUnicodeInfo::IsLowSurrogate(ch))
				{
					goto Return;
				}
			}
			SetLastError(XML_InvalidCharacter, (char)val, (m_ps->m_pChars[m_ps->m_nCharPos + 2] == 'x') ? m_ps->m_nCharPos + 3 : m_ps->m_nCharPos + 2);
			return -1;
		}

Return:
		BFX_ASSERT(nPos > 0);
		if (bExpand)
		{
			if (pInternalSubsetBuilder != NULL)
			{
				pInternalSubsetBuilder->Append(m_ps->m_pChars, m_ps->m_nCharPos, nPos - m_ps->m_nCharPos + 1);
			}
			pChars[nPos-1] = (UCHAR)high;
			pChars[nPos] = (UCHAR)low;
		}
		nCharCount = 2;
		return nPos + 1;
	}
}

// Parses named character entity reference (&amp; &apos; &lt; &gt; &quot;).
// Returns -1 if the reference is not a character entity reference.
// Returns -2 if more data is needed in the buffer
// Otherwise 
//      - replaces the last character of the entity reference (';') with the referenced character (if expand == true)
//      - returns position of the end of the character reference, that is of the character next to the original ';'
int REFXMLTextReaderImpl::ParseNamedCharRefInline(int nStartPos, bool bExpand, REFUniStringBuilder* pInternalSubsetBuilder)
{
	BFX_ASSERT(nStartPos < m_ps->m_nCharsUsed);
	BFX_ASSERT(m_ps->m_pChars[nStartPos] == '&');
	BFX_ASSERT(m_ps->m_pChars[nStartPos + 1] != '#');

	int nPos = nStartPos + 1;
	UCHAR* pChars = m_ps->m_pChars;
	char ch;

	switch (pChars[nPos])
	{
		// &apos; or &amp; 
	case 'a':
		nPos++;
		// &amp;
		if (pChars[nPos] == 'm')
		{
			if (m_ps->m_nCharsUsed - nPos >= 3)
			{
				if (pChars[nPos+1] == 'p' && pChars[nPos+2] == ';')
				{
					nPos += 3;
					ch = '&';
					goto FoundCharRef;
				}
				else
				{
					return -1;
				}
			}
		}
		// &apos;
		else if (pChars[nPos] == 'p')
		{
			if (m_ps->m_nCharsUsed - nPos >= 4)
			{
				if (pChars[nPos+1] == 'o' && pChars[nPos+2] == 's' && pChars[nPos+3] == ';')
				{
						nPos += 4;
						ch = '\'';
						goto FoundCharRef;
				}
				else
				{
					return -1;
				}
			}
		}
		else if (nPos < m_ps->m_nCharsUsed)
		{
			return -1;
		}
		break;
		// &guot;
	case 'q':
		if (m_ps->m_nCharsUsed - nPos >= 5)
		{
			if (pChars[nPos+1] == 'u' && pChars[nPos+2] == 'o' && pChars[nPos+3] == 't' && pChars[nPos+4] == ';')
			{
				nPos += 5;
				ch = '"';
				goto FoundCharRef;
			}
			else
			{
				return -1;
			}
		}
		break;
		// &lt;
	case 'l':
		if (m_ps->m_nCharsUsed - nPos >= 3)
		{
			if (pChars[nPos+1] == 't' && pChars[nPos+2] == ';')
			{
				nPos += 3;
				ch = '<';
				goto FoundCharRef;
			}
			else
			{
				return -1;
			}
		}
		break;
		// &gt;
	case 'g':
		if (m_ps->m_nCharsUsed - nPos >= 3)
		{
			if (pChars[nPos+1] == 't' && pChars[nPos+2] == ';')
			{
				nPos += 3;
				ch = '>';
				goto FoundCharRef;
			}
			else
			{
				return -1;
			}
		}
		break;
	default:
		return -1;
	}

	// need more data in the buffer
	return -2;

FoundCharRef:
	BFX_ASSERT(nPos > 0);
	if (bExpand)
	{
		if (pInternalSubsetBuilder != NULL)
		{
			pInternalSubsetBuilder->Append(m_ps->m_pChars, m_ps->m_nCharPos, nPos - m_ps->m_nCharPos);
		}
		m_ps->m_pChars[nPos-1] = ch;
	}
	return nPos;
}

REFXMLTextReaderImpl::EntityType REFXMLTextReaderImpl::HandleEntityReference(bool bIsInAttributeValue, EntityExpandType expandType, OUT int& nCharRefEndPos)
{
	BFX_ASSERT(m_ps->m_pChars[m_ps->m_nCharPos] == '&');

	if (m_ps->m_nCharPos + 1 == m_ps->m_nCharsUsed)
	{
		if (ReadData() == 0)
		{
			SetLastError(XML_UnexpectedEOF);
			return EntityTypeError;
		}
	}

	// numeric characters reference
	if (m_ps->m_pChars[m_ps->m_nCharPos+1] == '#')
	{
		EntityType entityType;
		nCharRefEndPos = ParseNumericCharRef(expandType != EntityExpandTypeOnlyGeneral, NULL, OUT entityType);
		BFX_ASSERT(entityType == EntityTypeCharacterDec || entityType == EntityTypeCharacterHex);
		return entityType;
	}
	// named reference
	else
	{
		// named character reference
		nCharRefEndPos = ParseNamedCharRef(expandType != EntityExpandTypeOnlyGeneral, NULL);
		if (nCharRefEndPos >= 0)
		{
			return EntityTypeCharacterNamed;
		}

		// general entity reference
		if (expandType != EntityExpandTypeOnlyCharacter && m_entityHandling == XMLEntityHandling_ExpandEntities)
		{
			// NOTE: validating reader compatibility mode: expand all entities in attribute values
			// general entity reference

			int nEndPos = ParseName();
			if (nEndPos < 0)
			{
				BFX_ASSERT(IsErrorOccurred());
				SetLastError(XML_ErrorParsingEntityName);
				return EntityTypeError;
			}

			// check ';'
			if (m_ps->m_pChars[nEndPos] != ';')
			{
				SetLastError(XML_UnexpectedToken, ";", nEndPos);
				return EntityTypeError;
			}

			int nEntityLinePos = m_ps->GetLinePos();

			UniString strEntityName = m_pNameTable->Add(UniString(m_ps->m_pChars + m_ps->m_nCharPos, nEndPos - m_ps->m_nCharPos));
			m_ps->m_nCharPos = nEndPos + 1;
			nCharRefEndPos = -1;

			EntityType entType = HandleGeneralEntityReference(strEntityName, bIsInAttributeValue, false, nEntityLinePos);
			m_strReportedBaseUri = m_ps->m_strBaseUri;
			m_strReportedEncoding = m_ps->m_strEncoding;
			return entType;
		}
		else
		{
			return EntityTypeUnexpanded;
		}
	}
}

// returns true == continue parsing
// return false == unexpanded external entity, stop parsing and return
REFXMLTextReaderImpl::EntityType REFXMLTextReaderImpl::HandleGeneralEntityReference(const UniString& strName, bool bIsInAttributeValue,
	bool bPushFakeEntityIfNullResolver, int nEntityStartLinePos)
{
	REF<REFXMLSchemaEntity> pEntity = NULL;
	REF<REFXMLQualifiedName> pQName = new REFXMLQualifiedName(strName);

	// TODO: parse dtd from context.

	if (m_pDtdParserProxy == NULL || !m_pDtdParserProxy->GetDtdSchemaInfo()->GetGeneralEntities()->Lookup(pQName, pEntity))
	{
		if (true/*m_bDisableUndeclaredEntityCheck*/)
		{
			pEntity = new REFXMLSchemaEntity(new REFXMLQualifiedName(strName), false);
			pEntity->SetText(UniString());	// set empty.
		}
		else
		{
			SetLastError(XML_UndeclaredEntity, CSTR(strName), m_ps->m_nLineNo, nEntityStartLinePos);
			return EntityTypeError;
		}
	}
	
	if (pEntity->IsProcessed())
	{
		SetLastError(XML_RecursiveGenEntity, CSTR(strName), m_ps->m_nLineNo, nEntityStartLinePos);
		return EntityTypeError;
	}

	if (!pEntity->GetNData()->IsEmpty())
	{
		if (true/*m_bDisableUndeclaredEntityCheck*/)
		{
			pEntity = new REFXMLSchemaEntity(new REFXMLQualifiedName(strName), false);
			pEntity->SetText(UniString());	// empty
		}
		else
		{
			SetLastError(XML_UnparsedEntityRef, CSTR(strName), m_ps->m_nLineNo, nEntityStartLinePos);
			return EntityTypeError;
		}
	}

	if (m_bStandalone && pEntity->IsDeclaredInExternal())
	{
		SetLastError(XML_ExternalEntityInStandAloneDocument, CSTR(strName), m_ps->m_nLineNo, nEntityStartLinePos);
		return EntityTypeError;
	}

	if (pEntity->IsExternal())
	{
		if (bIsInAttributeValue)
		{
			SetLastError(XML_ExternalEntityInAttValue, CSTR(strName), m_ps->m_nLineNo, nEntityStartLinePos);
			return EntityTypeError;//EntityTypeSkipped;
		}

		if (m_parsingMode == ParsingModeSkipContent)
		{
			return EntityTypeSkipped;
		}

		// TODO: process external entity.
		// PushInternalEntity

		BFX_ENSURE (false);
		return EntityTypeError;
	}
	else
	{
		if (m_parsingMode == ParsingModeSkipContent)
		{
			return EntityTypeSkipped;
		}

		int nEntityId = m_nNextEntityId++;
		PushInternalEntity(pEntity, nEntityId);

		m_pCurNode->m_nEntityId = nEntityId;

		return EntityTypeExpanded;
	}
}

// return true if EndEntity node should be reported. The entity is stored in lastEntity.
bool REFXMLTextReaderImpl::HandleEntityEnd(bool bCheckEntityNesting)
{
	if (m_nParsingStateStackTop == 0)
	{
		BFX_ASSERT(false);
		SetLastError(XML_InternalError);
		return false;
	}

	// TODO: entityResolvedManually

	if (bCheckEntityNesting)
	{
		if (m_ps->m_nEntityId != m_pNodes[m_nCurNodeIndex]->m_nEntityId)
		{
			SetLastError(XML_IncompleteEntity);
			return false;
		}
	}

	PopEntity();
	m_pCurNode->m_nEntityId = m_ps->m_nEntityId;

	m_strReportedEncoding = m_ps->m_strEncoding;
	m_strReportedBaseUri = m_ps->m_strBaseUri;

	return false;
}

//
// Read methods.
//
// Reads more data to the character buffer, discarding already parsed chars / decoded bytes.
int REFXMLTextReaderImpl::ReadData()
{
	// Append Mode:  Append new bytes and characters to the buffers, do not rewrite them. Allocate new buffers
	//               if the current ones are full
	// Rewrite Mode: Reuse the buffers. If there is less than half of the char buffer left for new data, move
	//               the characters that has not been parsed yet to the front of the buffer. Same for bytes.
	if (m_ps->m_bIsEof)
	{
		return 0;
	}

	if (m_ps->m_bAppendMode)
	{
		// the character buffer is full -> allocate a new one
		if (m_ps->m_nCharsUsed == m_ps->m_nCharsLength - 1)
		{
			int nNewCharsLength = m_ps->m_nCharsLength * 2;
			UCHAR* pNewChars = new UCHAR[nNewCharsLength];
			memcpy(pNewChars, m_ps->m_pChars, m_ps->m_nCharsLength * sizeof(UCHAR));
			delete[] m_ps->m_pChars;
			m_ps->m_pChars = pNewChars;
			m_ps->m_nCharsLength = nNewCharsLength;
		}
	}
	else
	{
		int nCharsLen = m_ps->m_nCharsLength;
		if (nCharsLen - m_ps->m_nCharsUsed <= nCharsLen / 2)
		{
			// move unparsed characters to front, unless the whole buffer contains unparsed characters
			int nCopyCharsCount = m_ps->m_nCharsUsed - m_ps->m_nCharPos;
			if (nCopyCharsCount < nCharsLen - 1)
			{
				m_ps->m_nLineStartPos = m_ps->m_nLineStartPos - m_ps->m_nCharPos;
				if (nCopyCharsCount > 0)
				{
					memmove(m_ps->m_pChars, m_ps->m_pChars + m_ps->m_nCharPos, nCopyCharsCount * sizeof(UCHAR));
				}
				m_ps->m_nCharPos = 0;
				m_ps->m_nCharsUsed = nCopyCharsCount;
			}
			else
			{
				int nNewCharsLength = m_ps->m_nCharsLength * 2;
				UCHAR* pNewChars = new UCHAR[nNewCharsLength];
				memcpy(pNewChars, m_ps->m_pChars, m_ps->m_nCharsLength * sizeof(UCHAR));
				delete[] m_ps->m_pChars;
				m_ps->m_pChars = pNewChars;
				m_ps->m_nCharsLength = nNewCharsLength;
			}
		}
	}
	int nCharsRead;
	if (m_ps->m_pTextReader != NULL)
	{
		// read chars
		nCharsRead = m_ps->m_pTextReader->Read(m_ps->m_pChars, m_ps->m_nCharsUsed,
				m_ps->m_nCharsLength - m_ps->m_nCharsUsed - 1);
		m_ps->m_nCharsUsed += nCharsRead;
	}
	else
	{
		nCharsRead = 0;
	}
	if (nCharsRead == 0)
	{
		BFX_ASSERT(m_ps->m_nCharsUsed < m_ps->m_nCharsLength);
		m_ps->m_bIsEof = true;
	}
	m_ps->m_pChars[m_ps->m_nCharsUsed] = 0;
	return nCharsRead;
}

void REFXMLTextReaderImpl::ShiftBuffer(int nSrcPos, int nDestPos, int nCount)
{
	// PERF: never use memcpy, should be memmove.
	memmove(m_ps->m_pChars + nDestPos, m_ps->m_pChars + nSrcPos, nCount * sizeof(UCHAR));
}

// NOTE: returns new position, or -1 if failed.
int REFXMLTextReaderImpl::ReadDataInName(int nPos)
{
	int iOffset = nPos - m_ps->m_nCharPos;
	bool bNewDataRead = (ReadData() != 0);
	nPos = m_ps->m_nCharPos + iOffset;
	return bNewDataRead ? nPos : -1;
}

int REFXMLTextReaderImpl::EatWhitespaces(REFUniStringBuilder* sb)
{
	int nPos = m_ps->m_nCharPos;
	int nWsCount = 0;
	UCHAR* pChars = m_ps->m_pChars;

	for (;;)
	{
		for (;;)
		{
			switch (pChars[nPos])
			{
			case 0xA:
				nPos++;
				OnNewLine(nPos);
				continue;
			case 0xD:
				if (pChars[nPos + 1] == 0xA)
				{
					int tmp1 = nPos - m_ps->m_nCharPos;
					if (sb != NULL)
					{
						sb->Append(pChars, m_ps->m_nCharPos, tmp1);
					}
					nPos += 2;
				}
				else if (nPos + 1 < m_ps->m_nCharsUsed || m_ps->m_bIsEof)
				{
					nPos++;
				}
				else
				{
					goto ReadData;
				}
				OnNewLine(nPos);
				continue;
			case 0x9:
			case 0x20:
				nPos++;
				continue;
			default:
				if (nPos == m_ps->m_nCharsUsed)
				{
					goto ReadData;
				}
				else
				{
					int tmp2 = nPos - m_ps->m_nCharPos;
					if (tmp2 > 0)
					{
						if (sb != NULL)
						{
							sb->Append(m_ps->m_pChars, m_ps->m_nCharPos, tmp2);
						}
						m_ps->m_nCharPos = nPos;
						nWsCount += tmp2;
					}
					return nWsCount;
				}
			}
		}

ReadData:
		int tmp3 = nPos - m_ps->m_nCharPos;
		if (tmp3 > 0)
		{
			if (sb != NULL)
			{
				sb->Append(m_ps->m_pChars, m_ps->m_nCharPos, tmp3);
			}
			m_ps->m_nCharPos = nPos;
			nWsCount += tmp3;
		}

		if (ReadData() == 0)
		{
			if (m_ps->m_nCharsUsed - m_ps->m_nCharPos == 0)
			{
				return nWsCount;
			}
			// NOTE: Is it necessary???
			if (m_ps->m_pChars[m_ps->m_nCharPos] != (UCHAR) 0xD)
			{
				BFX_ASSERT(false); // We should never get to this point.
				SetLastError(XML_UnexpectedToken);
				return -1;
			}
			BFX_ASSERT(m_ps->m_bIsEof);
		}
		nPos = m_ps->m_nCharPos;
		pChars = m_ps->m_pChars;
	}
	BFX_ASSERT1(false, "should never run at this point.");
	return -1;
}


//
// NodeData helpers
//
REFXMLTextReaderImpl::NodeData* REFXMLTextReaderImpl::AddAttribute(int nEndNamePos, int nColonPos)
{
	// setup attribute name
	if (nColonPos == -1 || !m_bSupportNamespaces)
	{
		UniString strLocalName = m_pNameTable->Add(m_ps->m_pChars, m_ps->m_nCharPos, nEndNamePos - m_ps->m_nCharPos);
		return AddAttribute(strLocalName, UniString(), strLocalName);
	}
	else
	{
		m_bAttrNeedNamespaceLookup = true;
		int nStartPos = m_ps->m_nCharPos;
		int nPrefixLen = nColonPos - nStartPos;
		if (nPrefixLen == m_strLastPrefix.GetLength() && UniString(m_ps->m_pChars + nStartPos, nPrefixLen) == m_strLastPrefix)
		{
			return AddAttribute(m_pNameTable->Add(m_ps->m_pChars, nColonPos + 1, nEndNamePos - nColonPos - 1), m_strLastPrefix, UniString());
		}
		else
		{
			UniString strPrefix = m_pNameTable->Add(m_ps->m_pChars, nStartPos, nPrefixLen);
			m_strLastPrefix = strPrefix;
			return AddAttribute(m_pNameTable->Add(m_ps->m_pChars, nColonPos + 1, nEndNamePos - nColonPos - 1), strPrefix, UniString());
		}
	}
}

REFXMLTextReaderImpl::NodeData* REFXMLTextReaderImpl::AddAttribute(const UniString& strLocalName, const UniString& strPrefix, const UniString& strNameWPrefix)
{
	NodeData* pNewAttr = AddNode(m_nCurNodeIndex + m_nAttrCount + 1, m_nCurNodeIndex + 1);

	// set attribute name
	pNewAttr->SetNamedNode(XMLNodeType_Attribute, strLocalName, strPrefix, strNameWPrefix);

	// TODO: pre-check attribute for duplicate: hash by first local name char

	m_nAttrCount++;
	return pNewAttr;
}

REFXMLTextReaderImpl::NodeData* REFXMLTextReaderImpl::AddAttributeNoChecks(const UniString& strName, int nAttrDepth)
{
	NodeData* pNewAttr = AddNode(m_nCurNodeIndex + m_nAttrCount + 1, nAttrDepth);
	pNewAttr->SetNamedNode(XMLNodeType_Attribute, m_pNameTable->Add(strName));
	m_nAttrCount++;
	return pNewAttr;
}

REFXMLTextReaderImpl::NodeData* REFXMLTextReaderImpl::AddNode(int nNodeIndex, int nNodeDepth)
{
	BFX_ASSERT(nNodeIndex < m_nNodesLength);
	BFX_ASSERT(m_pNodes[m_nNodesLength - 1] == NULL);

	NodeData* pNode = m_pNodes[nNodeIndex];
	if (pNode == NULL)
	{
		// growup if necessary.
		if (nNodeIndex >= m_nNodesLength - 1)
		{
			int nOldLength = m_nNodesLength;
			m_nNodesLength *= 2;
			REF<NodeData>* pNewNodes = new REF<NodeData>[m_nNodesLength];
			CopyObjects(pNewNodes, m_pNodes, nOldLength);
			delete[] m_pNodes;
			m_pNodes = pNewNodes;
		}
		BFX_ASSERT(nNodeIndex < m_nNodesLength);

		// allocate new node.
		pNode = new NodeData();
		m_pNodes[nNodeIndex] = pNode;
	}

	pNode->m_nDepth = nNodeDepth;

	return pNode;
}

void REFXMLTextReaderImpl::ResetAttributes()
{
	if (m_bFullAttrCleanup)
	{
		FullAttributeCleanup();
	}
	m_nCurAttrIndex = -1;
	m_nAttrCount = 0;
	// TODO: attrHashtable = 0;
	// TODO: attrDuplWalkCount = 0;
}

void REFXMLTextReaderImpl::FullAttributeCleanup()
{
	for (int i = 0; i < m_nCurNodeIndex + m_nAttrCount + 1; i ++)
	{
		NodeData* pAttr = m_pNodes[i];
		pAttr->m_pNextAttrValueChunk = NULL;
		pAttr->SetDefaultAttribute(false);
	}
	m_bFullAttrCleanup = false;
}

//
// ParsingState helpers.
//
void REFXMLTextReaderImpl::PushParsingState()
{
	if (m_pParsingStateStack == NULL)
	{
		// first time initialization.
		BFX_ASSERT(m_nParsingStateStackLength == 0 && m_nParsingStateStackTop == -1);
		m_nParsingStateStackLength = 16;
		m_pParsingStateStack = new REF<ParsingState>[m_nParsingStateStackLength];
	}
	else if (m_nParsingStateStackTop + 1 == m_nParsingStateStackLength)
	{
		// grow up.
		BFX_ASSERT(m_nParsingStateStackLength > 0);
		int nOldLength = m_nParsingStateStackLength;
		m_nParsingStateStackLength *= 2;
		REF<ParsingState>* pNewParsingStateStack = new REF<ParsingState>[m_nParsingStateStackLength];
		CopyObjects(pNewParsingStateStack, m_pParsingStateStack, nOldLength);
		delete[] m_pParsingStateStack;
		m_pParsingStateStack = pNewParsingStateStack;
	}
	// push stack.
	m_ps = new ParsingState();
	m_pParsingStateStack[++m_nParsingStateStackTop] = m_ps;
}

void REFXMLTextReaderImpl::PopParsingState()
{
	BFX_ASSERT(m_nParsingStateStackTop >= 0 && m_pParsingStateStack != NULL && m_nParsingStateStackLength > 0);
	m_pParsingStateStack[m_nParsingStateStackTop--] = NULL;
	m_ps = (m_nParsingStateStackTop > -1) ? m_pParsingStateStack[m_nParsingStateStackTop] : NULL;
}

bool REFXMLTextReaderImpl::PushInternalEntity(REFXMLSchemaEntity* pEntity, int nEntityId)
{
	BFX_ASSERT(!pEntity->IsExternal());

	PushParsingState();

	InitStringInput(pEntity->GetDeclaredURI(), m_ps->m_strEncoding, pEntity->GetText());

	m_ps->m_pEntity			= pEntity;
	m_ps->m_nEntityId		= nEntityId;
	m_ps->m_nLineNo			= pEntity->GetLineNumber();
	m_ps->m_nLineStartPos	= - pEntity->GetLinePosition() - 1;

	pEntity->SetProcessed(true);
	return true;
}

bool REFXMLTextReaderImpl::PopEntity()
{
	if (m_ps->m_pEntity != NULL)
	{
		m_ps->m_pEntity->SetProcessed(false);
	}
	PopParsingState();
	m_pCurNode->m_nEntityId = m_ps->m_nEntityId;
	return true;
}

// Returns the whitespace node type according to the current whitespace handling setting and xml:space
XMLNodeType REFXMLTextReaderImpl::GetWhitespaceType()
{
	// TODO: whitespace handling
	return XMLNodeType_None;
}

XMLNodeType REFXMLTextReaderImpl::GetTextNodeType(int orChars)
{
	if (orChars > 0x20)
	{
		return XMLNodeType_Text;
	}
	else
	{
		return GetWhitespaceType();
	}
}

void REFXMLTextReaderImpl::OnNewLine(int nPos)
{
	m_ps->m_nLineNo++;
	m_ps->m_nLineStartPos = nPos - 1;
}

void REFXMLTextReaderImpl::OnEof()
{
	BFX_ASSERT(m_ps->m_bIsEof);
	m_pCurNode = m_pNodes[0];
	m_pCurNode->Clear(XMLNodeType_None);
	m_pCurNode->SetLineInfo(m_ps->m_nLineNo, m_ps->GetLinePos());

	m_parsingFunction = ParsingFunctionEof;
	m_readState = ReadState_EndOfFile;

	m_strReportedEncoding.SetEmpty();
}

void REFXMLTextReaderImpl::OnDefaultNamespaceDecl(NodeData* pAttr)
{
	if (!m_bSupportNamespaces)
	{
		return;
	}

	UniString ns = m_pNameTable->Add(pAttr->m_strValue);
	pAttr->m_strNs = m_pNameTable->Add(USTR("http://www.w3.org/2000/xmlns/"));	// NsXmlNs

#if	0
	// TODO: xml context.
	if (!m_pCurNode->m_bXmlContextPushed)
	{
		PushXmlContext();
	}
#endif
	m_pXmlContext->m_strDefaultNamespace = ns;

	AddNamespace(UniString(), ns, pAttr);
}

void REFXMLTextReaderImpl::OnNamespaceDecl(NodeData* pAttr)
{
	if (!m_bSupportNamespaces)
	{
		return;
	}
	UniString strNs = m_pNameTable->Add(pAttr->m_strValue);
	if (strNs.IsEmpty())
	{
		SetLastError(XML_BadNamespaceDecl, pAttr->m_lineInfo2.m_nLineNo, pAttr->m_lineInfo2.m_nLinePos - 1);
		return;
	}
	AddNamespace(pAttr->m_strLocalName, strNs, pAttr);
}

void REFXMLTextReaderImpl::OnXmlReservedAttribute(NodeData* pAttr)
{
	if (pAttr->m_strLocalName == USTR("space"))
	{
		// xml:space
#if	0
		if (!m_pCurNode->m_bXmlContextPushed)
		{
			PushXmlContext();
		}
#endif
		UniString strTrimValue = pAttr->m_strValue.Trim();
		if (strTrimValue == USTR("preserve"))
		{
			m_pXmlContext->m_xmlSpace = XMLSpacePreserve;
		}
		else if (strTrimValue == USTR("default"))
		{
			m_pXmlContext->m_xmlSpace = XMLSpaceDefault;
		}
		else
		{
			SetLastError(XML_InvalidXmlSpace, CSTR(pAttr->m_strValue), pAttr->m_lineInfo.m_nLineNo, pAttr->m_lineInfo.m_nLinePos);
		}
	}
	else if (pAttr->m_strLocalName == USTR("lang"))
	{
		// xml:lang
#if	0
		if (!m_pCurNode->m_bXmlContextPushed)
		{
			PushXmlContext();
		}
#endif
		m_pXmlContext->m_strXmlLang = pAttr->m_strValue;
	}
}

void REFXMLTextReaderImpl::AddNamespace(const UniString& strPrefix, const UniString& strUri, NodeData* pAttr)
{
	if (strUri == USTR("http://www.w3.org/2000/xmlns/"))	// NsXmlNs
	{
		if (strPrefix == USTR("http://www.w3.org/XML/1998/namespace"))	//	XmlNs
		{
			SetLastError(XML_XmlnsPrefix, pAttr->m_lineInfo2.m_nLineNo, pAttr->m_lineInfo2.m_nLinePos);
		}
		else
		{
			SetLastError(XML_NamespaceDeclXmlXmlns, CSTR(strPrefix), pAttr->m_lineInfo2.m_nLineNo, pAttr->m_lineInfo2.m_nLinePos);
		}
		return;
	}
	else if (strUri == USTR("http://www.w3.org/XML/1998/namespace"))	//	NsXml
	{
		if (strPrefix != m_strXml && !m_bV1Compat)
		{
			SetLastError(XML_NamespaceDeclXmlXmlns, CSTR(strPrefix), pAttr->m_lineInfo2.m_nLineNo, pAttr->m_lineInfo2.m_nLinePos);
			return;
		}
	}
	if (strUri.IsEmpty() && !strPrefix.IsEmpty())
	{
		SetLastError(XML_BadNamespaceDecl, pAttr->m_lineInfo2.m_nLineNo, pAttr->m_lineInfo2.m_nLinePos);
		return;
	}

	if (!m_pNamespaceManager->AddNamespace(strPrefix, strUri))
	{
		BFX_ASSERT1(false, "Expecting ArgumentException with message Xml_XmlPrefix or Xml_XmlnsPrefix.");
		return;
	}
#if DEBUG
	if (strPrefix.IsEmpty())
	{
		BFX_ASSERT(m_pXmlContext->m_strDefaultNamespace == strUri);
	}
#endif
}

// Returns NamespaceURI associated with the specified prefix in the current namespace scope.
UniString REFXMLTextReaderImpl::LookupNamespace(const UniString strPrefix)
{
	if (!m_bSupportNamespaces)
	{
		return UniString();
	}

	return m_pNamespaceManager->LookupNamespace(strPrefix);
}

UniString REFXMLTextReaderImpl::LookupNamespace(NodeData* pNode)
{
	UniString ns = m_pNamespaceManager->LookupNamespace(pNode->m_strPrefix);
	if (ns.IsEmpty())
	{
		SetLastError(XML_UnknownNs, CSTR(pNode->m_strPrefix), pNode->GetLineNo(), pNode->GetLinePos());
	}
	return ns;
}

void REFXMLTextReaderImpl::ElementNamespaceLookup()
{
	BFX_ASSERT(m_pCurNode->m_type == XMLNodeType_Element);
	if (m_pCurNode->m_strPrefix.IsEmpty())
	{
		m_pCurNode->m_strNs = m_pXmlContext->m_strDefaultNamespace;
	}
	else
	{
		m_pCurNode->m_strNs = LookupNamespace(m_pCurNode);
	}
}

void REFXMLTextReaderImpl::AttributeNamespaceLookup()
{
	for (int i = m_nCurNodeIndex + 1; i < m_nCurNodeIndex + m_nAttrCount + 1; i++)
	{
		NodeData* pat = m_pNodes[i];
		if (pat->m_type == XMLNodeType_Attribute && !pat->m_strPrefix.IsEmpty())
		{
			pat->m_strNs = LookupNamespace(pat);
		}
	}
}

// If on attribute, moves to the element that contains the attribute node
bool REFXMLTextReaderImpl::MoveToElement()
{
	if (IsInAttributeValueIterator())
	{
		if (!FinishAttributeValueIterator())
			return false;
	}
	else if (m_pCurNode->m_type != XMLNodeType_Attribute)
	{
		return false;
	}
	m_nCurAttrIndex = -1;
	m_pCurNode = m_pNodes[m_nCurNodeIndex];

	return true;
}

// Moves to the first attribute of the current node
bool REFXMLTextReaderImpl::MoveToFirstAttribute()
{
	if (m_nAttrCount == 0)
	{
		return false;
	}
	if (IsInAttributeValueIterator())
	{
		if (!FinishAttributeValueIterator())
			return false;
	}

	m_nCurAttrIndex = 0;
	m_pCurNode = m_pNodes[m_nCurNodeIndex + 1];

	return true;
}

// Moves to the next attribute of the current node
bool REFXMLTextReaderImpl::MoveToNextAttribute()
{
	if (m_nCurAttrIndex + 1 < m_nAttrCount)
	{
		if (IsInAttributeValueIterator())
		{
			FinishAttributeValueIterator();
		}
		m_pCurNode = m_pNodes[m_nCurNodeIndex + 1 + ++m_nCurAttrIndex];
		return true;
	}
	return false;
}
// Moves to an attribute at the specified index (position)
void REFXMLTextReaderImpl::MoveToAttribute(int i)
{
	BFX_ENSURE(i >= 0 && i >= m_nAttrCount);

	if (IsInAttributeValueIterator())
	{
		FinishAttributeValueIterator();
	}

	m_nCurAttrIndex = i;
	m_pCurNode = m_pNodes[m_nCurNodeIndex + 1 + m_nCurAttrIndex];
}

// Moves to an attribute with the specified Name
bool REFXMLTextReaderImpl::MoveToAttribute(const UniString& strName)
{
	int i;
	if (strName.IndexOf(':') == -1)
	{
		i = GetIndexOfAttributeWithoutPrefix(strName);
	}
	else
	{
		i = GetIndexOfAttributeWithPrefix(strName);
	}

	if (i >= 0)
	{
		if (IsInAttributeValueIterator())
		{
			FinishAttributeValueIterator();
		}
		m_nCurAttrIndex = i - m_nCurNodeIndex - 1;
		m_pCurNode = m_pNodes[i];
		return true;
	}
	else
	{
		return false;
	}
}

// Moves to an attribute with the specified LocalName and NamespceURI
bool REFXMLTextReaderImpl::MoveToAttribute(const UniString& strLocalName, const UniString& strNamespaceURI)
{
	for (int i = m_nCurNodeIndex + 1; i < m_nCurNodeIndex + m_nAttrCount + 1; i++)
	{
		if (m_pNodes[i]->m_strLocalName == strLocalName && m_pNodes[i]->m_strNs == strNamespaceURI)
		{
			m_nCurAttrIndex = i - m_nCurNodeIndex - 1;
			m_pCurNode = m_pNodes[i];

			if (IsInAttributeValueIterator())
			{
				FinishAttributeValueIterator();
			}
			return true;
		}
	}
	return false;
}

// Iterates through the current attribute value's text and entity references chunks.
bool REFXMLTextReaderImpl::ReadAttributeValue()
{
	if (m_parsingFunction != ParsingFunctionInReadAttributeValue)
	{
		if (m_pCurNode->m_type != XMLNodeType_Attribute)
		{
			return false;
		}
		if (m_readState != ReadState_Interactive || m_nCurAttrIndex < 0)
		{
			return false;
		}
		if (m_parsingFunction == ParsingFunctionInReadValueChunk)
		{
			// TODO: FinishReadValueChunk();
			BFX_ASSERT(false);
		}
		if (m_parsingFunction == ParsingFunctionInReadContentAsBinary)
		{
			// TODO: FinishReadContentAsBinary();
			BFX_ASSERT(false);
		}

		if (m_pCurNode->m_pNextAttrValueChunk == NULL || m_entityHandling == XMLEntityHandling_ExpandEntities)
		{
			NodeData* pSimpleValueNode = AddNode(m_nCurNodeIndex + m_nAttrCount + 1, m_pCurNode->m_nDepth + 1);
			pSimpleValueNode->SetValueNode(XMLNodeType_Text, m_pCurNode->m_strValue);
			pSimpleValueNode->m_lineInfo = m_pCurNode->m_lineInfo2;
			pSimpleValueNode->m_nDepth = m_pCurNode->m_nDepth + 1;
			pSimpleValueNode->m_pNextAttrValueChunk = NULL;
			m_pCurNode = pSimpleValueNode;
		}
		else
		{
			m_pCurNode = m_pCurNode->m_pNextAttrValueChunk;

			// Place the current node at nodes[nodeIndex + attrCount + 1]. If the node type is be EntityReference and user
			// calls ResolveEntity, the associated EndEntity node will be constructed from the information stored there.

			// This will initialize the (nodeIndex + attrCount + 1) place in nodes array
			AddNode(m_nCurNodeIndex + m_nAttrCount + 1, m_nCurNodeIndex + 2);
			m_pNodes[m_nCurNodeIndex + m_nAttrCount + 1] = m_pCurNode;

			m_bFullAttrCleanup = true;
		}
		m_nextParsingFunction = m_parsingFunction;
		m_parsingFunction = ParsingFunctionInReadAttributeValue;
		m_nAttributeValueBaseEntityId = m_ps->m_nEntityId;
		return true;
	}
	else
	{
		if (m_ps->m_nEntityId == m_nAttributeValueBaseEntityId)
		{
			if (m_pCurNode->m_pNextAttrValueChunk != NULL)
			{
				m_pCurNode = m_pCurNode->m_pNextAttrValueChunk;
				m_pNodes[m_nCurNodeIndex + m_nAttrCount + 1] = m_pCurNode;	// if m_pCurNode == EntityReference node, it will be picked from here by SetupEndEntityNodeInAttribute
				return true;
			}
			return false;
		}
		else
		{
			// expanded entity in attribute value
			return ParseAttributeValueChunk();
		}
	}
}

//
// Private implementation methods & properties
//
bool REFXMLTextReaderImpl::IsInAttributeValueIterator()
{
	return m_nAttrCount > 0 && m_parsingFunction >= ParsingFunctionInReadAttributeValue;
}

bool REFXMLTextReaderImpl::FinishAttributeValueIterator()
{
	BFX_ASSERT(IsInAttributeValueIterator());
	if (m_parsingFunction == ParsingFunctionInReadValueChunk)
	{
		// TODO: FinishReadValueChunk();
		BFX_ASSERT(false);
	}
	else if (m_parsingFunction == ParsingFunctionInReadContentAsBinary)
	{
		// TODO: FinishReadContentAsBinary();
		BFX_ASSERT(false);
	}
	if (m_parsingFunction == ParsingFunctionInReadAttributeValue)
	{
		while (m_ps->m_nEntityId != m_nAttributeValueBaseEntityId)
		{
			HandleEntityEnd(false);
			if (IsErrorOccurred())
				return false;
		}
		m_parsingFunction = m_nextParsingFunction;
		m_nextParsingFunction = (m_nCurNodeIndex > 0) ? ParsingFunctionElementContent : ParsingFunctionDocumentContent;
		m_bEmptyEntityInAttributeResolved = false;
	}
	return true;
}

int REFXMLTextReaderImpl::GetIndexOfAttributeWithoutPrefix(const UniString& strName)
{
	if (strName.IsEmpty())
	{
		return -1;	// is it right???
	}
	for (int i = m_nCurNodeIndex + 1; i < m_nCurNodeIndex + m_nAttrCount + 1; i++)
	{
		if (m_pNodes[i]->m_strLocalName == strName && m_pNodes[i]->m_strPrefix.IsEmpty())
		{
			return i;
		}
	}
	return -1;
}

int REFXMLTextReaderImpl::GetIndexOfAttributeWithPrefix(const UniString& strName)
{
	if (strName.IsEmpty())
	{
		return -1;
	}
	for (int i = m_nCurNodeIndex + 1; i < m_nCurNodeIndex + m_nAttrCount + 1; i++)
	{
		if (m_pNodes[i]->m_strNameWPrefix == strName)
		{
			return i;
		}
	}
	return -1;
}

}	//	namespace BFX
